等待很长的过程并仍在更新UI

我一直在尝试创建一个写入数据库的任务,而不会阻塞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); 

-要么-

 while (bProcessIsRunning) Thread.Sleep(1); 

它会阻止UI。

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

如果我同步运行该进程,我仍然需要创建某种方式来更新UI(在我看来,一个DoEvents调用),所以它似乎没有被锁定。

我究竟做错了什么?

C#使用事件模型 – 您要做的是调度执行工作的进程,然后让该进程在完成时触发自定义事件或使用其中一个线程事件。 当进程在“后台”释放控件中运行时,从代码中返回到系统。

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

其次,你使用冲突的术语。

等待==阻止

您说您不想阻止UI线程,但您确实希望等待任务完成。 这些是相互排斥的状态。 如果你正在等待某些事情完成,你就会阻止你的线程。

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

但似乎你只是想要更新UI,而不是可用。 通常只有在您希望进度条或其他UI动画继续移动时才有意义。 在这种情况下,我会打开一个modal dialog,启动我的任务,然后等待它,是的,调用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(); 

modal dialog阻止用户执行任何操作,但任何动画仍然可以工作,因为DoEvents()每秒调用20次(或更多)。

(您可能希望为不同的任务完成状态添加特殊处理,但这不是主题。)

你不能在UI线程上等待。

而是向Exited事件添加处理程序。

我不知道这是否会简化您的问题,但我们使用Essential Objects控件: http : //www.essentialobjects.com/Products/EOWeb/来管理我们的长流程。

如果将long db操作委托给后台工作线程,则通过从worker触发ProgressChangedEvent来指示进度,您可以处理更新UI中的进度条。 通过触发RunWorkerCompleteEvent发出相同的完成信号。

无需轮询/循环执行所需的事件。

问题是当你的后台线程正在做一些你不允许在表单中做的事情。

关闭它,更改编辑框? 通过某种状态机完成的操作可能很简单,只需在关闭线程时禁用按钮然后在RunWorkerCompleted事件中再次启用它。 你可以单独留下buutom并在它的Click处理程序中检查一个名为busy的布尔值。

您是在卸载流程以显示进度还是仅仅是为了避免“Windiows没有响应”,或者是否存在用户在中间过程中可以明智地做的其他事情,例如关闭表单,取消操作等。

一旦你弄清楚UI应该做什么,你就可以将背景工程事件挂钩到一些能够管理事物的代码中。