Living .NET...

Musings on .NET, and the like - Manoj G [MVP, Connected Systems Developer]

BackgroundWorker and SynchronizationContext

I have mentioned umpteen number of times earlier - .NET 2.0 comes with a wealth of new classes that have been added to the BCL. Today’s turn is of BackgroundWorker. If you look at the available documentation (circa Beta 2), this is what you will find:

“Executes an operation on a separate thread”. You first reaction would be “Heck, what’s so “new” about that?” Okay, so let’s spelunk into its properties and methods and try to find out.

 

Methods

RunWorkerAsync

Starts the designated method on a separate threadpool thread.

CancelAsync

Requests the cancellation of the pending background operation

Events

ProgressedChanged

This is called when the ReportProgress method is called

RunWorkerCompleted

Occurs when the background operation is completed

DoWork

Occurs when the RunAsync method is called.

 

Again, after looking at these, you thoughts wouldn’t change – “Still, what’s so great about the same? I had created similar helpers in .NET 1.1.”. Fine, let me tell what’s new. The very interesting aspect about the BackgroundWorker is the fact that RunWorkerCompleted and ProgressChanged event handlers would execute on the thread that created the BackgroundWorker object – most typically on a Windows form main thread (the thread on which the UI controls are created). Therefore, you now do not fall into a trap that you used to fall into earlier – updating a winform control on the wrong thread and facing unpredictable results. (Note that the DoWork method runs on a separate threadpool thread – do not update UI controls on that method)

 

So, you would now say: “Cool, the BackgroundWorker probably has a reference to the ISynchronizeInvoke object, and it would call Invoke on it to dispatch the method on the right thread. (System.Windows.Forms.Contrtol class implements the ISynchronizeInvoke interface) ”.

If you look closely, the BackgroundWorker is actually agnostic of the environment it runs in. It could not even be a winform application, or even if it was, it does not really rely on ISynchronizeInvoke directly. So, how does this magic happen?

 

With the help of the valuable services of Reflector, I came to know about a new class (a set of them actually) introduced in .NET 2.0, which

are responsible for this magic – SynchronizationContext. Here’s what the documentation says:

 

Provides the basic functionality for propagating a synchronization context in various synchronization models.  The purpose of the synchronization model implemented by this class is to allow the internal asynchronous/synchronization operations of the common language runtime (CLR) to behave properly with different synchronization models. This model also simplifies some of the requirements that managed applications have had to follow in order to work correctly under different synchronization environments

 

Can’t gather much from this explanation isn’t it? What is does mean perhaps is no more Control.Invoke. But it appears as if it is analogous to the thread’s Context object we had earlier, and a bit more specialized. By now, it would have rung another bell with you. As of .NET 2.0, in the case of Thread.Start method, the security context of the creating thread would be passed to the newly created thread. Guess what is behind this?

 

I tried a hand at this new class, simple stuff actually. I created a thread from the button click handler on a winform app and updated a control from the thread method (through anymymous method in this case). This is the snippet I tried:

 

private void button1_Click(object sender, EventArgs e)
{
    ctx = SynchronizationContext.Current;
    
// thanks to anonymous methods, everything here is inline
    // thanks to me, this is confusing
    
ThreadPool.QueueUserWorkItem(
        
delegate(object state)
        {
            
// would be true
            
Debug.WriteLine(Thread.CurrentThread.IsThreadPoolThread.ToString());
            ctx.Send(
delegate(object someState)
            {
                
// would be false
                
label1.Text = Thread.CurrentThread.IsThreadPoolThread.ToString();
            },
null);
        }
    );
}

  

Send is the method of the SynchronizationContext class to dispatch the method on the right thread synchronously. To do the same asynchronously, we call Post.

 

Another interesting thing to note is - in .NET 2.0, if you update a control on the wrong thread, you get an InvalidOperationException. As of 1.1, there was no exception, but the result was unpredicatable.

 

As you might have already guessed, there is not much documentation yet. But, unlike ContextBoundObject, ContextAttribute and other related classes, you might be seeing a lot of articles and proper documentation in the near future. At least, I shall spelunk a little more and come with an article of sorts.

Posted: Thu, Nov 3 2005 10:40 by Manoj G | with 5 comment(s)
Filed under:

Comments

shayke said:

Hi
Is this the best way to switch context between threads??

Shayke
# December 18, 2005 5:31 AM

. said:

The ProgressChanged and RunWorkerCompleted Events will not allways be handled on the thread the BackgroundWorker was created on.

This depends on the SynchronizationContext of the thread where the Backgroundworker was created on. For Windows.Forms threads this is true, because the WindowsFormsSynchronization Context actually uses Control.Invoke and Control.BeginInvoke to marshall calls to the coresponding thread.

But from a thread with just has a standard SynchronizationContext, the send and post method of this context do not marshall the call to a specific thread!

Regards

# December 28, 2006 5:14 AM

Željko said:

This works for winforms apps only, since there is only one implementation of sync context. In a console application, SynchronizationContext.Current is always null.
# April 24, 2007 7:35 AM

Threading in .Net 2.0 said:

Pingback from  Threading in .Net 2.0

# January 15, 2008 7:04 AM

Martin Golding said:

>>But from a thread with just has a standard

>>SynchronizationContext, the send and post method of

>>this context do not marshall the call to a specific

>>thread!

Does a standard SynchronizationContext enable the ability to marshall to the original thread?

All of my test code says no, If I output Thread.GetHashCode() and write test code I can see when using ctx.Post, it always uses the running thread ID and not the one I want to marshal to.

# February 14, 2008 8:12 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)