When to call SynchronizationContext.SetSynchronizationContext() in a UI application?

Sylvain picture Sylvain · Jan 11, 2011 · Viewed 8.6k times · Source

I'm learning about the SynchronizationContext class. I'm trying to understand what are the common usage scenarios for calling SynchronizationContext.SetSynchronizationContext() in the context of a WinForm/WPF application. What does it mean to set the SynchronizationContext of a thread? When should I do it and why? Also, if I set it, should I unset it at some point?

Edit:

In his answer, @Hans Passant asked why I was contemplating SetSynchronizationContext(). The idea I have is to set the context on a worker thread so that code running on that thread will have a context to use.

private void button3_Click(object sender, EventArgs e)
{
    var syncContext = SynchronizationContext.Current;
    Task.Factory.StartNew(() =>
    {
        // Setup the SynchronizationContext on this thread so 
        // that SomeAsyncComponentThatNeedsACurrentContext
        // will have a context when it needs one
        if (SynchronizationContext.Current == null)
            SynchronizationContext.SetSynchronizationContext(syncContext);

        var c = new SomeAsyncComponentThatNeedsACurrentContext();
        c.DoSomething();

    });
}

Answer

Hans Passant picture Hans Passant · Jan 11, 2011

You should in general leave it up to the specific UI class library to set this correctly. Winforms automatically installs a WindowsFormsSynchronizationContext instance, WPF installs a DispatcherSynchronizationContext, ASP.NET installs a AspNetSynchronizationContext, a Store app installs WinRTSynchronizationContext, etcetera. Highly specific synchronization providers that are tuned to the way the UI thread dispatches events.

There's something special about the way these application environments use their main thread. They all implement a dispatcher loop and use a thread-safe queue to receive notifications. Generally known as the "message loop" in Windows GUI programming. This is a generic solution to the producer/consumer problem, with the dispatcher loop implementing the consumer.

Creating your own synchronization provider for a worker thread first requires that such a thread implements this same mechanism. In other words, you will need a thread-safe queue, like ConcurrentQueue, and the thread needs to be written to retrieve notifications from the queue and execute them. A delegate object would be a good choice. You will now have no problem implementing the Post method, simply add the SendOrPostCallback delegate to the queue. Extra work is required to implement the Send method, the thread needs to signal back that the delegate was retrieved and executed. So the queue object also needs an AutoResetEvent.

Do note how your thread now stops becoming a generally useful thread, it is bogged down by having to dispatch these notifications. And how the existing synchronization providers already do all of this. So if your app is a Winforms app then you might as well call Application.Run() on your worker thread with a dummy invisible form. And you'll automatically get its synchronization provider for free.