等待很长时间,仍在更新用户界面

Tre*_*son 3 .net c# multithreading doevents winforms

我一直在尝试创建一个写入数据库的任务,而不会阻塞UI线程。我遇到的最大问题是等待该过程完成而不会发生阻塞。

我一直在尝试避免使用DoEvents(尽管此程序现在经常使用它,但我希望在前进时不再使用它)。

我试图创建要在第二个线程上运行的进程,并等待它完成以及使用BackgroundWorker

我遇到的问题不是让代码在其他线程中运行,而是试图找到一种方法来等待它完成。

基本上,现在我要执行以下操作:

  1. 连接到数据库
  2. 创建一个后台工作程序(或线程)来写数据库(我可能最终会写成,BackgroundWorker所以我可以使用ReportProgress
  3. 启动线程或 BackgroundWorker
  4. 使用While循环等待线程/ BackgroundWorker完成。对于线程,我等待IsAlive变为false,对于BackgroundWorker,我切换一个布尔变量。
  5. 我让用户知道该过程已完成。

问题出在#4中。

在没有代码的情况下执行while循环,或者Thread.Sleep(0)阻塞UI(Thread.Sleep(0)使程序也占用程序资源的100%)

所以我做:

while (!thread.IsAlive)
   Thread.Sleep(1);
Run Code Online (Sandbox Code Playgroud)

-要么-

while (bProcessIsRunning)
   Thread.Sleep(1);
Run Code Online (Sandbox Code Playgroud)

这会阻塞用户界面。

如果在此调用Application.DoEvents(),则UI会更新(尽管它是可单击的,所以在此过程运行时,我必须禁用整个表单)。

如果我同步运行该过程,那么我仍然需要创建某种方式来更新UI(在我看来,是一个DoEvents调用),这样它就不会被锁定。

我究竟做错了什么?

Igb*_*man 5

首先,为什么要避免DoEvents()

其次,您使用的是相互矛盾的术语。

等待==阻止

你说你希望阻止UI线程,但你做的要等待任务完成。这些是互斥状态。如果您正在等待完成某件事,那么就阻塞了线程。

如果您希望UI实际上是可用的(不被阻止),那么您就不必等待任务完成。只需注册一个事件处理程序即可在完成时触发。例如,使用BackgroundWorker处理RunWorkerCompleted事件。对于Task,您可以使用延续将回调分配给您的主线程。

但是似乎您只希望更新UI,而不是可用的。通常,只有在您希望进度条或其他UI动画继续移动时才有意义。在这种情况下,我将打开一个模式对话框,启动我的任务,然后等待,是的,调用DoEvents()。

    var dialog = new MessageBoxFormWithNoButtons("Please wait while I flip the jiggamawizzer");
    dialog.Shown += (_, __) =>
        {
            var task = Task.Factory.StartNew(() => WriteToDatabase(), TaskCreationOptions.LongRunning);
            while (!task.Wait(50))  // wait for 50 milliseconds (make shorter for smoother UI animation)
                Application.DoEvents(); // allow UI to look alive
            dialog.Close();
        }

    dialog.ShowDialog();
Run Code Online (Sandbox Code Playgroud)

模态对话框阻止用户执行任何操作,但是由于DoEvents()每秒被调用20次(或更多次),因此任何动画仍然可以使用。

(您可能想为不同的任务完成状态添加特殊处理,但这是不合时宜的。)