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.
精彩评论