views:

281

answers:

1

A winforms dialog is using BackgroundWorker to perform some asynchronous operations with significant success. On occasion, the async process being run by the background worker will need to raise events to the winforms app for user response (a message that asks the user if they wish to cancel), the response of which captured in an CancelEventArgs type of the event.

Being an implementation of threading, I would have expected the RaiseEvent of the worker to fire, and then the worker would continue, hence requiring me to pause the worker until the response is received. Instead however, the worker is held to wait for the code executed by the raise event to complete.

It seems like method I am calling via the event call is actually on the worker thread used by the background worker, and I am surprised, since I expected to see it on the Main Thread which is where the mainform is running. Also surprisingly, there are no cross thread exceptions thrown.

Can somebody please explain why this is not as I expect?

+2  A: 

The BackgroundWorker will raise its ProgressChanged event and RunWorkerCompleted event on the UI thread (more accurately, it will post them to the thread using the currently established SynchronizationContext.)

But it will not simply enable you to raise arbitrary events on the UI thread. For that you should access the SynchronizationContext.Current and use the Post method.

Josh Einstein
Thanks Josh, looks good and I'll check it out tomorrow. I am still surprised that no cross thread exception is raised when I call Windows.Forms.Messagebox.Show from that event, given it is still on the background thread.
Topdown
There is no such thing as a "UI thread", there just happens to be a thread that you're only running UI stuff from. Any thread can call UI stuff like MessageBox or create and display WinForms. Likewise, your worker thread doesn't really know that its being run async so events are raised just as if it were being run normally.
Chris Haas
Technically Chris is correct however calling MessageBox.Show from a thread that does not currently have a message loop will cause one to be created (otherwise MessageBox wouldn't work) so I agree that it would be nice if there was a check for illegal cross-thread access the way Windows Forms controls work.
Josh Einstein
BTW, you can generally tell right away when you've called MessageBox from a background thread because it will not be visually styled the way other controls are. The buttons will be "flat" pre-XP style.
Josh Einstein
Hi Josh, from the comments above I gather that MessageBox.Show will never throw a cross thread exception. If that is correct, do you see any undesirable side effects by showing the messagebox in my current manner. This will save the need for using the post method and also locking the background thread until response is returned from the messagebox via CancelEventArgs. Of course, if richer UI than a messagebox was required then we'd need to do all that.
Topdown
Yes, it is a problem because of a few reasons. 1) Like I said it requires setting up a message loop which means you could very well have message loops running on several threads! 2) The visual styles will be all messed up and depending on timing, this could in theory screw up the styling of certain controls on the UI thread. 3) Even if you show a modal message box on a background thread the UI thread will still be fully interactive. In other words, the modality will be useless.
Josh Einstein
It's very easy in C# to use anonymous methods to make a call like this on the UI thread. I don't know the equivalent VB.NET syntax but maybe this would give you an idea: SynchronizationContext.Current.Send(delegate{ MessageBox.Show("hi"); }. null); // this blocks the bg and ui thread so be careful of deadlocks.
Josh Einstein
If you don't want to block the background thread, just use SynchronizationContext.Post instead of Send.
Josh Einstein
Great, thanks for the detailed response Josh.
Topdown