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,这可能会导致一些减速。我将尝试将其放在自己的后台工作者中。

Backgroundworker/Control.BeginInvoke() freezing UI

虽然您当然可以在 DoWork 方法中调用更新 UI(这是您对BeginInvoke的调用正在执行的操作),但您不应该这样做,否则,您真的不需要 BackgroundWorker 类。

相反,您将调用 ReportProgress 方法,该方法随后将触发 ProgressChanged 事件。

ProgressChanged 事件的事件处理程序中,您将调用 Install.Enabled = false

该问题与BeginInvoke的使用没有直接关系。这可能更多地与它的调用量有关。您可以将ReportProgressProgressChanged 事件结合使用,但假设您将所有BeginInvoke调用替换为ReportProgress,那么您可能会遇到类似的问题,因为BackgroundWorker将使用与 BeginInvoke/Invoke 使用的相同机制自动封送 UI 线程上的事件处理程序。我会说快速的解决方案是减少您尝试更新 UI 的频率。

就个人而言,我认为使用 BeginInvoke 来更新带有工作线程进度的 UI 是过度使用的。同样,BackgroundWorker类也提倡这种次优方法。通常最好让工作线程定期将更新信息发布到共享数据结构,然后 UI 线程可以按自己的计划选取它(通常通过使用 Timer 轮询)。这有几个优点:

  • 它打破了 UI 和工作线程之间的紧密耦合,Control.Invoke'Control.BeginInvoke
  • 它将更新 UI 线程的责任放在它应该属于的 UI 线程上。
  • UI 线程可以指示何时以及多久进行一次更新。
  • 不存在 UI 消息泵溢出的风险,就像工作线程启动的封送处理技术一样。
  • 工作线程不必等待确认已执行更新,然后再继续执行后续步骤(即,在 UI 和工作线程上获得更多吞吐量)。

不幸的是,BackgroundWorker缺乏使用这种替代办法的必要机制。但是,只要您不过于频繁地调用它,您就可以使用 ReportProgress