Sonntag, 29. November 2009

BackgroundWorker in mixed-mode applications

If you have a mixed-mode application, it is very tempting to use the BackgroundWorker class even for tasks initiated from the native side. After all, you often have a good use-case for tasks running in the background, not even considering the performance gain from multi-core processors, and the BackgroundWorker provides a framework which is not easily got using only native code.

One of the big advantages of that class is that it automatically notifies you of completion (and progress) on the GUI thread, so you can show some result to the user. How does it do that? Internally, it resuses the message loop which every application in Windows has (it sends a special message). On a higher abstraction level, the mechanism provided by the .NET framework for such threading callbacks is called a SynchronizationContext. On CodeProject someone has a good description on how this works.

Now, the interesting thing is that there is a thread-local variable (SynchronizationContext::Current) which must be set before the BackgroundWorker is used. This variable is initially null, and it gets set by the first Control instance which is created.

So, no problem for the normal C# applications, but in a mixed-mode application, your main window and every other window may be native, so no .NET control is created, so the SynchronizationContext is still null ... Unfortunately, the BackgroundWorker doesn't throw a nice exception if that is the case. It just makes the notifications on the worker thread, which can lead to subtle errors and may only be detected if you try to modify some MFC GUI.

An easy workaround is to create a dummy control before the background worker is used. That control needs not be shown on the screen and it can be disposed immediately, just the constructor must have been called. In C++/CLI, the workaround is one line of code.

But be careful: I first created the control only if the synchronization context was null. But it turns out that some other .NET framework methods (e.g. Bitmap::GetHbitmap) set another synchronization context if it is still null when they are called. This other context seems to be a dummy which doesn't do anything, so the notifications will once again come on the worker thread.

So, to be sure, either create that dummy control always, or create it very early, while you're sure that no one else has tempered yet with the synchronization context. Of course, all that is not a problem if you have some .NET control or form shown anyway.


0 Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]

<< Startseite