Backgroundworker/Control.BeginInvoke() 冻结 UI

发布于 2024-11-06 03:57:28 字数 643 浏览 0 评论 0原文

我有一些代码执行 Windows svc (另一个进程)并同时更新 UI。这些调用使用 BeginInvoke,如下所示:

Install.BeginInvoke((MethodInvoker) delegate { Install.Enabled = false; });

这是在 DoWork 事件处理程序中。然而,用户界面仍然冻结。我是否需要在某处调用 Application.DoEvents(如果需要,在哪里?)?怎样才能消除冻结现象呢?

在按钮单击中,我有:

GetPrinterDto(DirectoriesTreeView.SelectedNode.Text);

InstallBackgoundWorker.RunWorkerAsync();

InstallBackgroundWorker 只是运行更新 UI 等的代码。

我想做的是调用 WCF 服务器(托管在 Windows 服务中 - 这很好),但是当它停止工作时,以异步方式更新进度条和具有任意值的标签。当我选择一个树节点时,所选树节点的事件处理程序被触发,这可能会导致速度减慢。我会尝试将其放入其自己的后台工作人员中。

I have some code which executes a windows svc (another process) and updates the UI at the same time. The calls use BeginInvoke, like so:

Install.BeginInvoke((MethodInvoker) delegate { Install.Enabled = false; });

This is in the DoWork event handler. However, the UI still freezes up. Do I need a call to Application.DoEvents somewhere (if so, where?)? How can I eliminate the freezing?

In the button click, I have:

GetPrinterDto(DirectoriesTreeView.SelectedNode.Text);

InstallBackgoundWorker.RunWorkerAsync();

InstallBackgroundWorker just runs the code which updates the UI etc.

What I am trying to do is call a WCF server (hosted in a windows service - this is fine), but while that is off doing its thing, update the progress bar and a label with arbitrary values in an async fashion. When I select a treenode, the event handler for selected treenode is f ired, which may cause some of the slowdown. I will try putting this in its own backgroundworker.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

悲欢浪云 2024-11-13 03:57:28

虽然您当然可以在 DoWork 方法中调用更新 UI(这就是对 BeginInvoke 的调用所做的事情),但您应该不,否则,您真的不需要 BackgroundWorker 类。

相反,您可以调用 ReportProgress 方法然后将触发 ProgressChanged 事件

您可以在 ProgressChanged 事件的事件处理程序中调用 Install.Enabled = false

While you certainly could place the call to update the UI (which is what your call to BeginInvoke is doing) in the DoWork method, you shouldn't, otherwise, you really wouldn't need the BackgroundWorker class.

Rather, you would call the ReportProgress method which would then fire the ProgressChanged event.

It's in the event handler of the ProgressChanged event that you would make the call to Install.Enabled = false.

秋叶绚丽 2024-11-13 03:57:28

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

就我个人而言,我认为使用 BeginInvoke 来更新 UI 的工作线程进度已经被过度使用了。同样,BackgroundWorker 类也推广了这种次优方法。通常最好让工作线程定期向共享数据结构发布更新信息,然后 UI 线程可以按照自己的计划获取更新信息(通常通过使用计时器进行轮询)。这有几个优点:

  • 它打破了 Control.Invoke\Control.BeginInvoke 强加的 UI 和工作线程之间的紧密耦合。
  • 它将更新 UI 线程的责任放在了无论如何都应该属于的 UI 线程上。
  • UI 线程可以决定更新的时间和频率。
  • 不存在 UI 消息泵溢出的风险,就像工作线程启动的封送技术的情况一样。
  • 工作线程在继续下一步之前不必等待更新已执行的确认(即,您在 UI 和工作线程上获得更多吞吐量)。

不幸的是,BackgroundWorker 缺乏使用这种替代方法的必要机制。但是,只要您不太频繁地调用它,您应该可以使用ReportProgress

The problem is not directly related to the use of BeginInvoke. It probably has more to do with how much it is being called. You could use ReportProgress in conjunction with the ProgressChanged event, but assuming you replace all of your BeginInvoke calls with ReportProgress then you may wind up with similar problems because BackgroundWorker will automatically marshal the event handlers on the UI thread using the same mechanisms that BeginInvoke/Invoke are using anyway. I would say the quick solution is to reduce the frequency at which you are attempting to update the UI.

Personally, I think the use of BeginInvoke to update the UI with worker thread progress is way overused. And in the same regard the BackgroundWorker class promotes this suboptimal method as well. It is usually better to have the worker thread publish update information periodically to a shared data structure and then the UI thread can pick it up on its own schedule (usually by polling using a Timer). This has several advantages:

  • It breaks the tight coupling between the UI and worker threads that the Control.Invoke\Control.BeginInvoke imposes.
  • It puts the responsibility of updating the UI thread on the UI thread where it should belong anyway.
  • The UI thread gets to dictate when and how often the update should take place.
  • There is no risk of the UI message pump being overrun as would be the case with the marshaling techniques initiated by the worker thread.
  • The worker thread does not have to wait for an acknowledgement that the update was performed before proceeding with its next steps (ie. you get more throughput on both the UI and worker threads).

It is unfortunate that BackgroundWorker lacks the necessary mechanisms for using this alternate approach. However, you should be okay with using ReportProgress as long as you do not call it too frequently.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文