开发者

Backgroundworker/Control.BeginInvoke() freezing UI

开发者 https://www.devze.com 2023-03-03 19:25 出处:网络
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:

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开发者_C百科 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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消