BackgroundWorker RunWorkerCompleted Event
My C# application has several background workers. Sometimes one background worker will fire off another. When the first background worker completes and the RunWorkerCompleted
event is fired, on which thread will that event fire, the UI or the first background worker from which RunWorkerAsync
was called? 开发者_StackOverflow社区 I am using Microsoft Visual C# 2008 Express Edition. Any thoughts or suggestions you may have would be appreciated. Thanks.
If the BackgroundWorker
was created from the UI thread, then the RunWorkerCompleted
event will also be raised on the UI thread.
If it was created from a background thread, the event will be raised on an undefined background thread (not necessarily the same thread, unless you're using a custom SynchronizationContext
).
Interestingly, this doesn't seem to be all that well-documented on MSDN. The best reference I was able to find was here:
The preferred way to implement multithreading in your application is to use the BackgroundWorker component. The BackgroundWorker component uses an event-driven model for multithreading. The background thread runs your DoWork event handler, and the thread that creates your controls runs your ProgressChanged and RunWorkerCompleted event handlers. You can call your controls from your ProgressChanged and RunWorkerCompleted event handlers.
From my observation, the RunWorkerCompleted is executed on the thread that called RunWorkerAsync, which is not necessarily the thread that CREATED the background worker.
On same issue found this thread msdn.
It's all about the SynchronizationContext.
Windows Forms will automatically overwrite any existing SynchronizationContext by default when it creates a window; see WindowsFormsSynchronizationContext.AutoInstall: http://msdn.microsoft.com/en-us/library/system.windows.forms.windowsformssynchronizationcontext.autoinstall.aspx
BackgroundWoker captures the SynchronizationContext when RunWorkerAsync is called, not when it is constructed (note that this is an implementation detail, not documented). It then uses that captured SynchronizationContext to execute RunWorkerCompleted.
So, the thread that runs RunWokerCompleted is actually determined by SynchronizationContext.Current at the time RunWorkerAsync is called.
WPF provides a DispatcherSynchronizationContext that will marshal the call to its UI thread. Windows Forms provides a WindowsFormsSynchronizationContext that will marshal the call to its UI thread. When doing WPF/Forms interop, both systems share a single UI thread. You mentioned this is all in the context of an MFC app; in this case, MFC doesn't provide a SynchronizationContext (of course), but it would share its thread with the WPF and Forms thread (they all share a single UI thread).
One more piece of information: the default SynchronizationContext will queue actions (e.g., RunWorkerCompleted) to the ThreadPool. This sounds like the behavior you're seeing. This default behavior kicks in if SynchronizationContext.Current is null at the time RunWorkerAsync is called.
So, it sounds like closing out the Windows Forms form may be clearing SynchronizationContext.Current. Windows Forms does have the notion of a "main form", so it may be doing that since it thinks the last form of the application just closed.
I recommend: 1) Testing if SynchronizationContext.Current is in fact null at the time RunWorkerCompleted is called, and possibly also testing before and after the Windows Forms form is displayed. Just to make sure this is the problem. 2) Setting SynchronizationContext.Current (via SynchronizationContext.SetSynchronizationContext) after the Windows Forms form closes. Pass "new DispatcherSynchronizationContext()" as the argument.
Alternatively, if the Windows Forms form is a modal dialog, you can use the ScopedSynchronizationContext class from my Nito.Async library (http://www.codeplex.com/NitoAsync), which is a very simple class used to temporarily replace SynchronizationContext.Current, resetting it to its original value at the end of its "using" block.
More information on the various SynchronizationContext types is on my blog: http://nitoprograms.blogspot.com/2009/10/synchronizationcontext-properties.html
. In short insert
AsyncOperationManager.SynchronizationContext = new DispatcherSynchronizationContext(this.Dispatcher)
before RunWorkerAsync call.
精彩评论