Backgroundworker/Control.BeginInvoke() freezing UI
本文关键字:freezing UI BeginInvoke Control Backgroundworker | 更新日期: 2023-09-27 17:55:54
我有一些代码可以执行Windows svc(另一个进程)并同时更新UI。调用使用 BeginInvoke
,如下所示:
Install.BeginInvoke((MethodInvoker) delegate { Install.Enabled = false; });
这位于DoWork
事件处理程序中。但是,UI 仍然冻结。我需要打电话Application.DoEvents
某个地方(如果是,在哪里?如何消除冻结?
在按钮单击中,我有:
GetPrinterDto(DirectoriesTreeView.SelectedNode.Text);
InstallBackgoundWorker.RunWorkerAsync();
InstallBackgroundWorker
只是运行更新 UI 等的代码。
我正在尝试做的是调用WCF服务器(托管在Windows服务中 - 这很好),但是当它停止做它的事情时,以异步方式更新进度条和具有任意值的标签。当我选择一个树节点时,所选树节点的事件处理程序是f ired,这可能会导致一些减速。我将尝试将其放在自己的后台工作者中。
虽然您当然可以在 DoWork
方法中调用更新 UI(这是您对BeginInvoke
的调用正在执行的操作),但您不应该这样做,否则,您真的不需要 BackgroundWorker
类。
相反,您将调用 ReportProgress
方法,该方法随后将触发 ProgressChanged
事件。
ProgressChanged
事件的事件处理程序中,您将调用 Install.Enabled = false
。
该问题与BeginInvoke
的使用没有直接关系。这可能更多地与它的调用量有关。您可以将ReportProgress
与 ProgressChanged
事件结合使用,但假设您将所有BeginInvoke
调用替换为ReportProgress
,那么您可能会遇到类似的问题,因为BackgroundWorker
将使用与 BeginInvoke
/Invoke
使用的相同机制自动封送 UI 线程上的事件处理程序。我会说快速的解决方案是减少您尝试更新 UI 的频率。
就个人而言,我认为使用 BeginInvoke
来更新带有工作线程进度的 UI 是过度使用的。同样,BackgroundWorker
类也提倡这种次优方法。通常最好让工作线程定期将更新信息发布到共享数据结构,然后 UI 线程可以按自己的计划选取它(通常通过使用 Timer
轮询)。这有几个优点:
- 它打破了 UI 和工作线程之间的紧密耦合,
Control.Invoke'Control.BeginInvoke
。 - 它将更新 UI 线程的责任放在它应该属于的 UI 线程上。
- UI 线程可以指示何时以及多久进行一次更新。
- 不存在 UI 消息泵溢出的风险,就像工作线程启动的封送处理技术一样。
- 工作线程不必等待确认已执行更新,然后再继续执行后续步骤(即,在 UI 和工作线程上获得更多吞吐量)。
不幸的是,BackgroundWorker
缺乏使用这种替代办法的必要机制。但是,只要您不过于频繁地调用它,您就可以使用 ReportProgress
。