Monday, December 29, 2008

Understanding SynchronizationContext Part III

Introduction

This article is the last part of the three part series on SynchronizationContext. SynchronizationContext is a class introduced by .NET 2.0 with little documentation or explanation of how to use it. I have tried to explain in part one how to use this class, and in part two, how to create your own SynchronizationContext. In part two, I showed how to build a SynchronizationContext that will marshal code from any .NET thread into a STA thread. I have done this so I can execute COM code that needs to run on the STA thread. The next step is to create a WCF service that will execute all its service operations on the STA thread (using the SynchronizationContext I provided in part two). In this article, I will show you how to configure WCF to provide a custom STA SynchronizationContext so each method on a WCF service will execute on the same STA thread. This will allow me to provide a simple programming model that will be fully compatible with COM, and I will not need to worry about thread safety because all my code will run within the same thread.

WCF - The Power

I will not even try to give a full explanation of WCF here. WCF is vast in functionality, and does much more than just "Remoting" or web services. I am starting to believe that WCF should be treated as a runtime rather than a communication framework. WCF provides us the following programming models "out of the box":

  • built-in support for error handling
  • built-in support for concurrency
  • built-in support for security
  • built-in support for transactions
  • built-in support for data encryption
  • built-in support for durable services
  • built-in support for method interception and inspection (AOP)
  • built-in support for one way communication, and callbacks
  • and much more...

Just listen to this ARCast, where Juval Lowy believes every class should be a WCF class: Every Class a WCF Service, with Juval Lowy. At first, I said to myself, this is too much, every class to be a WCF class is just insane. But, the more I looked at WCF and what it had to offer, the more it made sense to me. WCF is a very extensible framework, allowing the developer to do a lot of custom configuration. For example, providing a SynchronizationContext for your service is something you will not be able to do within native .NET classes, but only in WCF (and that's just one feature among many). Although, I must say I did not go as far as making each class a WCF class. I have decided to make each component a WCF service. Even if I don't plan to run my component remotely, I still believe the benefits of the WCF runtime are worth coding my components as WCF services. Bottom line, WCF is more than web services or Remoting services, it provides a solid programming model that applies in every type of development. If you don't know WCF, I strongly recommend you learn it, it is by far one of the better frameworks Microsoft has released. For this article, I assume you have a basic knowledge of WCF services.

A Simple WCF Service

Let me just say this right away, I have coded a WCF service for this article just for testing. I do not recommend you code services the way that I have. To be more specific, in my code, the service implementation and the service contact are within the same assembly - this is not recommended. However, because I am dealing with testing my SynchronizationContext within a prototype/testing project, I wanted to keep the number of assemblies and code to a minimum. Normally, when I code a WCF service, I have three projects:

  • Project containing the service contact (interface) and possibly any data contacts (DTOs)
  • Project containing the service implementation
  • Project hosting the service (optional)

So please, no bashing, I am stating it here that the code is just for testing and not for production. Now that we got that out of the way, let me show the service contact and the implementation.

[ServiceContract]
public interface IStaService
{

  [OperationContract]
  string DoWorkOnSTAThread(string state);
}

There is really not much to say about this service, it is very simple. Let me show you the implementation of this service:

public class StaService : IStaService
{
  public string DoWorkOnSTAThread(string state)
  {
     ApartmentState aptState = Thread.CurrentThread.GetApartmentState();
     if (aptState == ApartmentState.STA)
        Trace.WriteLine("Using STA thread");

     int id = Thread.CurrentThread.ManagedThreadId;
     Trace.WriteLine("WCF current thread: " + id);
     return "processed by " + aptState.ToString() + " Thread id: " + id.ToString();
  }
}

Notes:

  • DoWorkOnSTAThread will be the method I plan to execute on the STA thread, so I coded some tracking code to make sure I am running this method on the STA thread. I check the ApartmentState and make sure it is an ApartmentState.STA.
  • I also log the thread ID; considering I want all my code to run on the same STA thread, the ID should always be the same.
  • However, I did not write any code to indicate using the STA Sync Context, so for now, STA thread marshaling is not used.

Testing our Service

We will be running our service a few times, so let's learn how to test it. My service is hosted using webdev, and it runs as a web service. To test it, I have used the WCF test client applicaiton. It can normally be found at "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\WcfTestClient.exe". By calling DoWorkOnSTAThread multiple times, I get the following output:

WCF current thread: 12
WCF current thread: 12
WCF current thread: 11
WCF current thread: 11
WCF current thread: 11
WCF current thread: 13
WCF current thread: 10
WCF current thread: 11
WCF current thread: 10
WCF current thread: 10
WCF current thread: 13

To find out the service behavior, you can view the ServiceDescription within a custom ServiceHost (I will show the custom service host code later in the article).

Notice that I am running the same method on multiple threads. This is because, by default, WCF created my service using a PerSession InstanceContextMode and a ConcurrencyMode of Single (see image above). This means that the method will be executed one at a time, but each time, it is executed by a thread assigned by the thread pool. First, you should avoid using PerSession for your services. PerSession simply does not scale, I consider it evil for coding scalable services, so, I am going to change the service behavior.

[ServiceBehavior(UseSynchronizationContext=true, 
 ConcurrencyMode=ConcurrencyMode.Multiple,
 InstanceContextMode=InstanceContextMode.PerCall)]
public class StaService : IStaService
{
  public string DoWorkOnSTAThread(string state)
  {
     ApartmentState aptState = Thread.CurrentThread.GetApartmentState();
     if (aptState == ApartmentState.STA)
        Trace.WriteLine("Using STA thread");

     int id = Thread.CurrentThread.ManagedThreadId;
     Trace.WriteLine("WCF current thread: " + id);
     //throw new Exception("boom");
     return "processed by " + aptState.ToString() + " Thread id: " + id.ToString();
  }
}

Using the WCF service behavior attribute [ServiceBehavior(UseSynchronizationContext=true, ConcurrencyMode=ConcurrencyMode.Multiple, InstanceContextMode=InstanceContextMode.PerCall)], I ask WCF to create this service using InstanceContextMode.PerCall. This means, every method call will create an instance of the service, and as soon as the method completes, the instance is destroyed. Using ConcurrencyMode.Multiple allows for clients to execute methods within this service concurrently, allowing multiple threads to execute the same method at the same time. UseSynchronizationContext=true means that I ask the service to use the SynchronizationContext attached to the host's thread. Using this new service behavior, let's see our output by executing the method three times:

WCF current thread: 12
WCF current thread: 9
WCF current thread: 6

Same results more or less... Our service can scale better, but still have no control on which thread the code is executed on.

Providing your Service a SynchronizationContext

You noticed that irrespective of whether the service is using single or multiple concurrency mode, in both, the invocation is controlled by WCF using different threads from the thread pool. In order to provide your own SynchronizationContext, you have two choices:

  • Set the SynchronizationContext on the hosting thread before opening the host
  • Create your own ServiceBehavior and override the SynchronizationContext on the service endpoint

I will explore the second option simply because we don't always create our own hosting program, and in many cases, the hosting is done in WAS or IIS. Let me show you the ServiceBehvior attribute I created to use STA thread synchronization.

public class StaServiceBehaviorAttribute : Attribute, IContractBehavior, IServiceBehavior
{
  StaSynchronizationContext mStaContext;
  public StaServiceBehaviorAttribute()
  {
     mStaContext = new StaSynchronizationContext();
  }
  #region IContractBehavior Members

  void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, 
    ServiceEndpoint endpoint, 
    System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
  {

  }

  void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription,
    ServiceEndpoint endpoint, 
    System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
  {

  }

  void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription,
    ServiceEndpoint endpoint,
    System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
  {
     dispatchRuntime.SynchronizationContext = mStaContext;
  }

  void IContractBehavior.Validate(ContractDescription contractDescription, 
    ServiceEndpoint endpoint)
  {

  }

  #endregion

  #region IServiceBehavior Members

  void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription,
    System.ServiceModel.ServiceHostBase serviceHostBase,
    System.Collections.ObjectModel.Collection endpoints,
    System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
  {

  }

  void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, 
    System.ServiceModel.
    ServiceHostBase serviceHostBase)
  {

  }

  void IServiceBehavior.Validate(ServiceDescription serviceDescription, 
    System.ServiceModel.ServiceHostBase serviceHostBase)
  {
     serviceHostBase.Closed += delegate
     {
        mStaContext.Dispose();
     };
  }

  #endregion
}

By creating my own custom ServiceBehvior attribute, I can set certain settings that are otherwise not available to me.

Notice that I am creating the StaSynchronizationContext at the constructor:

public StaServiceBehaviorAttribute()
{
    mStaContext = new StaSynchronizationContext();
}

The other most important part is implementing the ApplydispatchBehvior method. This method will be executed once on each endpoint within your service. It allows me to override the default SynchronizationContext with my own custom one.

void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription,
    ServiceEndpoint endpoint,
    System.ServiceModel.Dispatcher.DispatchRuntime dispatchRuntime)
{
    dispatchRuntime.SynchronizationContext = mStaContext;
}

To make sure my STA thread ends correctly, I have also modified the IServiceBehavior.Validate and provided an event handler for closing the host.

void IServiceBehavior.Validate(ServiceDescription serviceDescription,
     System.ServiceModel.ServiceHostBase serviceHostBase)
{
 serviceHostBase.Closed += delegate
 {
    mStaContext.Dispose();
 };
}

Using the STA Synchronization Context

Let's take another look at our service, now that we have created our own Service Behavior to apply a custom Synchronization Context.

[StaServiceBehaviorAttribute]
[ServiceBehavior(UseSynchronizationContext=true, ConcurrencyMode=ConcurrencyMode.Multiple,
 InstanceContextMode=InstanceContextMode.PerCall)]
public class StaService : IStaService
{
  public string DoWorkOnSTAThread(string state)
  {
     ApartmentState aptState = Thread.CurrentThread.GetApartmentState();
     if (aptState == ApartmentState.STA)
        Trace.WriteLine("Using STA thread");

     int id = Thread.CurrentThread.ManagedThreadId;
     Trace.WriteLine("WCF current thread: " + id);
     //throw new Exception("boom");
     return "processed by " + aptState.ToString() + " Thread id: " + id.ToString();
  }
}
  • Notice that I kept the WCF service behavior to use PerCall and ConcurrencyMode.Multiple. Considering we are planning to marshal our code to an STA thread, using ConcurrencyMode.Single will have the same behavior as ConcurrencyMode.Multiple.
  • Notice, I have placed my StaServiceBehaiorAttribue on the service. This will create the STA thread, and apply an STA synchronization context on all the endpoints my service exposes (in the examplen I only have one endpoint).

Very, well, let's try it. But using the WCF client tool, I have send multiple requests to my service, and here are the results:

Using STA thread
WCF current thread: 9
Using STA thread
WCF current thread: 9
Using STA thread
WCF current thread: 9
Using STA thread
WCF current thread: 9
Using STA thread
WCF current thread: 9
Using STA thread
WCF current thread: 9
Using STA thread
WCF current thread: 9
Using STA thread
WCF current thread: 9
  • Notice that all the calls to the WCF method are executed on thread 9
  • Notice that thread 9 is an STA thread as we expected

Now, I am able to control on which thread the WCF service is executing on.

A Word About Hosting

Notice that using this method, you can control the synchronization context of your service no matter what hosting method you choose. If you are hosting using a console application, you could set the synchronization context to STA (using the SynchronizationContext.SetSynchronizationContext). Before opening the host, it will have the same effect. I tried to use a custom ServiceHostFactory and set the synchronization context there, but it did not work. Let me show you the custom ServiceHostFactory and the custom ServiceHost.

public class StaCustomHostFactory : ServiceHostFactory
{
  protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
  {
     return new StaCustomHost(serviceType, baseAddresses);
  }
}
public class StaCustomHost : ServiceHost
{
  public StaCustomHost(Type serviceType, params Uri[] baseAddresses) :
     base(serviceType, baseAddresses)
  {

  }

  protected override void InitializeRuntime()
  {
     int id = Thread.CurrentThread.ManagedThreadId;
     base.InitializeRuntime();
  }

  protected override void ApplyConfiguration()
  {
     int id = Thread.CurrentThread.ManagedThreadId;
     // get the configuration from the app.config first
     base.ApplyConfiguration();

     // create the STA Thread Sync object and attach it to this hosting
     // thread.
     StaSynchronizationContext staContext = new StaSynchronizationContext();
     SynchronizationContext.SetSynchronizationContext(staContext);
  }
}
<%@ ServiceHost 
    Language="C#" Debug="true" 
    Factory="StaServiceHost.StaCustomHostFactory" 
    Service="TestStaService.StaService" 
    CodeBehind="StaService.svc.cs" %>

Notice, I set the SynchronizationContext within the ApplyConfiguration method. WCF will take whatever SynchronizationContext is set on the host's thread and use it for each invocation. However, it did not work at all. I don't know exactly why, but the only explanation I can come up with is that the code within ApplyConfiguration does not run within the same thread as ServiceHost.Open. Considering that the host is opened by webdev or IIS, I really don't know how to set the synchronization context on these threads. Therefore, I recommend you stick to using custom service behavior as I have shown before for overriding the synchronization context of your service's endpoints.

One little comment about closing the host. As you can see in part II, the STA thread is created as soon as the STA SynchronizationContext object is created. In this case, it is created within the service behavior. It is important that this thread ends when the host closes. That's the reason I have coded this event handler:

void IServiceBehavior.Validate(ServiceDescription serviceDescription,
     System.ServiceModel.ServiceHostBase serviceHostBase)
{
 serviceHostBase.Closed += delegate
 {
    mStaContext.Dispose();
 };
}

I have tested this code by stopping the webdev process and placing a break-point on the event handler. I validated that the STA thread is exiting when webdev is closing. Thanks for the small WCF miracles.

Is WCF using Send or Post?

You might be wondering if the Send or the Post of our sync-context is used. I really didn't know, so I set a breakpoint on both the Send and Post methods, and found out that WCF always uses the Post method. I have changed the service concurrency-mode to ConcurrencyMode.Single and still Post was used. So, when using Single or Multiple concurrency, in both cases, the Post method is used to marshal code into the STA thread. What about exceptions? Notice, I have modified my service to throw an exception:

[StaServiceBehaviorAttribute]
[ServiceBehavior(UseSynchronizationContext=true, 
 ConcurrencyMode=ConcurrencyMode.Multiple,
 InstanceContextMode=InstanceContextMode.PerCall)]
public class StaService : IStaService
{
  public string DoWorkOnSTAThread(string state)
  {
     ApartmentState aptState = Thread.CurrentThread.GetApartmentState();
     if (aptState == ApartmentState.STA)
        Trace.WriteLine("Using STA thread");

     int id = Thread.CurrentThread.ManagedThreadId;
     Trace.WriteLine("WCF current thread: " + id);
     throw new Exception("boom");
     //return "processed by " + aptState.ToString() + 
     //       " Thread id: " + id.ToString();
  }
}

Initially, I believed that this might cause the STA thread to terminate. It is an unhandled exception running on the STA thread, but WCF does handle the exception for you. So, the STA thread does not end even if you throw exceptions within the STA thread. WCF catches any unhandled exceptions, and converts them to FaultException on the client thread. This saves our STA thread from ending, so it keeps running regardless if exceptions are thrown. Thank God for another WCF miracle.

Conclusion

In this article, I got into the inner workings of WCF, and grabbed control on "where" a service method is executed on. I am able to "tell" WCF to marshal all the method calls on an STA thread, allowing us not to worry about calling COM objects that are designed to work on the STA thread. This will also allow you to pop a UI within your service method, but I really don't recommend it. By marshaling the code yourself, you can add additional logic to log and validate each invocation. You might even add security at this level, and refuse marshaling a call if it does not fit a certain criteria. However, do not use this as a wild card; service side method interception can also do the same thing. Still, now that you know about this feature, you might find a good use for it within your projects.

Thank you for reading, and happy .NETting.

Thursday, December 25, 2008

Understanding SynchronizationContext Part II

Introduction

In part one, I explained the role of SynchronizationContext within the .NET framework 2.0. It is mostly used to allow threads to communicate with the UI thread. We have learned in part one that the SynchronizationContext by itself does nothing to marshal code between threads; in fact, this class should be an abstract class. The .NET framework provided us a version of this class to marshal code into the UI thread, but what about coding your own version of SynchronizationContext to do something else? Sounds like a daunting task, but it is really not that bad. I will try to show in this article how to code your own SynchronizationContext for marshaling code from any thread into an STA thread. In part III of the series, I will leverage this class within the WCF framework. But, before we start running, we must learn to walk. So, let's start.

Switching work into STA threads

You might be wondering why I am coding this in the first place. STA is a threading model that is used by COM. A long time ago, before the age of .NET Remoting and WCF (sounds like hundred years ago), developers coded COM classes that could only run on Single Apartment Threads (STA). Why? Because the COM runtime would handle thread marshaling for the developer, and always made sure that your COM class would execute on the same thread. This way, the COM developer did not need to worry about multi-threading, Mutexes, Semaphores, Events, and all the other multi-threading toys out there. Just for the record, COM also provided a Multi Threading Apartment model (for the brave ones). With MTA, the developer had to worry about multi-threading issues, but had more control. There is a lot of documentation out there about MTA and STA, all you have to do is Google "STA MTA" and read all the history about it. Thank God, I don't need to code COM anymore. However, there is a lot of COM out there, and within our company, a lot of business logic is coded into COM classes that can only execute on an STA thread. To be able to call these classes from any thread in .NET, I decided to code a custom STA thread synchronization context. After all the sweat and work put into it, it felt right to share it with you, hoping some poor developer out there might find it useful. Although this article explains how to marshal code into an STA thread, you can take the information in this article and have your own sync context do something else.

How do we switch between two threads?

The first question is how would we manage marshaling between two running threads. This problem is typically solved by implementing some sort of a common communication block that both threads can read and write from. An ideal communication object between two threads is a queue. A queue provides us the ability to send work from one thread to another based on the invocation order. This is the typical Consumer / Provider model, where one thread plays the role of a consumer (reading from the queue), and another thread plays the role of a provider (writing items to the queue). To simplify things, let's see how this might work:

  • Thread 1: Sends a message to a common queue.
  • Thread 2: Listens for incoming messages from the common queue. In our case, Thread 2 will be an STA thread.

A closer look at the blocking queue

I wanted to have a queue to queue up work items from thread X to my STA thread. I also wanted my thread to dequeue items only when there are items in the queue. If there are no items, I want the Dequeue method to wait until something pops into the queue. Typically, this is called a "Blocking Queue". Let's see the code:

internal interface IQueueReader<t> : IDisposable
{
  T Dequeue();
  void ReleaseReader();
}

internal interface IQueueWriter<t> : IDisposable
{
  void Enqueue(T data);
}


internal class BlockingQueue<t> : IQueueReader<t>, 
                                     IQueueWriter<t>, IDisposable
{
  // use a .NET queue to store the data
  private Queue<t> mQueue = new Queue<t>();
  // create a semaphore that contains the items in the queue as resources.
  // initialize the semaphore to zero available resources (empty queue).
  private Semaphore mSemaphore = new Semaphore(0, int.MaxValue);
  // a event that gets triggered when the reader thread is exiting
  private ManualResetEvent mKillThread = new ManualResetEvent(false);
  // wait handles that are used to unblock a Dequeue operation.
  // Either when there is an item in the queue
  // or when the reader thread is exiting.
  private WaitHandle[] mWaitHandles;

  public BlockingQueue()
  {
    mWaitHandles = new WaitHandle[2] { mSemaphore, mKillThread };
  }
  public void Enqueue(T data)
  {
    lock (mQueue) mQueue.Enqueue(data);
    // add an available resource to the semaphore,
    // because we just put an item
    // into the queue.
    mSemaphore.Release();
  }

  public T Dequeue()
  {
    // wait until there is an item in the queue
    WaitHandle.WaitAny(mWaitHandles);
    lock (mQueue)
    {
      if (mQueue.Count > 0)
         return mQueue.Dequeue();
    }
    return default(T);
  }

  public void ReleaseReader()
  {
    mKillThread.Set();
  }


  void IDisposable.Dispose()
  {
    if (mSemaphore != null)
    {
      mSemaphore.Close();
      mQueue.Clear();
      mSemaphore = null;
    }
  }
}
  • Because this queue is used by multiple threads, notice that I am blocking access to the queue using the lock statement.
  • Normally, I block the Dequeue method until there is an item in the queue. The way this works is by having a semaphore that represents all the items in the queue as resources. When the semaphore is created for the first time, the queue is empty, and therefore there are no resources available, so calling Dequeue will block (notice there is zero at the first argument indicating no available resources, and a large number for the second argument representing the size of the queue).
    private Semaphore mSemaphore = new Semaphore(0, int.MaxValue);
  • Notice that when I dequeue an item, I block on an array of WaitHandles (WaitHandle.WaitAny(mWaitHandles);). This code means "Wait until there is a message, or until the read thread is marked to stop running."
  • I have not shown the actual thread yet, I will show it next. However, the STA thread will be the reading thread, spending most of its time waiting for a message on the queue or processing a message from the queue.
  • Notice that when a message is enqueued into the queue, it releases the semaphore, indicating that a resource is available; this will cause the Dequeue method to unblock.
  • The semaphore has a max limit of Int.Max; we should never reach anything close to this limit as long as the thread is dequeue-ing more of less as fast as it is enqueue-ing.

The SendOrPostCallbackItem class

Notice that the blocking queue class is generic, this was done in case I decide to re-use it in another application (and you are free to use it for your needs as well). So, what are we planning to put into this queue? Considering this queue is responsible to marshal code from one thread to another, the ideal item to queue is a delegate. Still, we need a little more than a delegate, and not just a simple delegate, but a SendOrPostCallback delegate.

internal enum ExecutionType
{
   Post,
   Send
}

internal class SendOrPostCallbackItem
{
   object mState;
   private ExecutionType mExeType;
   SendOrPostCallback mMethod;
   ManualResetEvent mAsyncWaitHandle = new ManualResetEvent(false);
   Exception mException = null;

   internal SendOrPostCallbackItem(SendOrPostCallback callback,
      object state, ExecutionType type)
   {
      mMethod = callback;
      mState = state;
      mExeType = type;
   }

   internal Exception Exception
   {
      get { return mException; }
   }

   internal bool ExecutedWithException
   {
      get { return mException != null; }
   }

   // this code must run ont the STA thread
   internal void Execute()
   {
      if (mExeType == ExecutionType.Send)
         Send();
      else
         Post();
   }

   // calling thread will block until mAsyncWaitHandle is set
   internal void Send()
   {
      try
      {
         // call the thread
         mMethod(mState);
      }
      catch (Exception e)
      {
         mException = e;
      }
      finally
      {
         mAsyncWaitHandle.Set();
      }
   }

   /// <summary />
   /// Unhandled exceptions will terminate the STA thread
   /// </summary />
   internal void Post()
   {
      mMethod(mState);
   }

   internal WaitHandle ExecutionCompleteWaitHandle
   {
      get { return mAsyncWaitHandle; }
   }
}
  • SendOrPostCallbackItem contains the delegate we wish to execute on the STA thread.
  • The Send and Post are really helper methods, they are both responsible for launching the code, and they are both designed to be called from the STA thread. However, because the Send is required to block, and to report exceptions back to the calling thread (non-STA thread), I use a ManualResentEvent when the execution is complete. I also keep track of the exception; if there is one, it will be thrown on the non-STA thread (producer thread).
  • Post is simple. It just calls the method, no need to notify when it is done, and there is no need to track the exception either.

Overall, this class is responsible for two main tasks. Storing the delegate to execute, and executing it in two possible modes: Send and Post. Send requires additional tracking (such as the exception and notification of complication). Post just executes the method without doing anything else. Normally, if Post is executed on the STA thread, any exceptions reported by the delegate will cause the thread to end. I will explain this more in part III of the article when I introduce WCF into the mix. But for now, just keep this issue in mind

The STA thread and all its glory

Finally, we can show and explain the meat and potatoes of this sync context. Now that we have a queue, and we know what we are planning to push into it, let's look at the STA thread (the thread responsible for marshaling code).

internal class StaThread
{
   private Thread mStaThread;
   private IQueueReader<sendorpostcallbackitem> mQueueConsumer;

   private ManualResetEvent mStopEvent = new ManualResetEvent(false);


   internal StaThread(IQueueReader<sendorpostcallbackitem> reader)
   {
      mQueueConsumer = reader;
      mStaThread = new Thread(Run);
      mStaThread.Name = "STA Worker Thread";
      mStaThread.SetApartmentState(ApartmentState.STA);
   }

   internal void Start()
   {
      mStaThread.Start();
   }


   internal void Join()
   {
      mStaThread.Join();
   }

   private void Run()
   {

      while (true)
      {
         bool stop = mStopEvent.WaitOne(0);
         if (stop)
         {
            break;
         }

        SendOrPostCallbackItem workItem = mQueueConsumer.Dequeue();
        if (workItem != null)
            workItem.Execute();
      }
   }

   internal void Stop()
   {
      mStopEvent.Set();
      mQueueConsumer.ReleaseReader();
      mStaThread.Join();
      mQueueConsumer.Dispose();

   }
}

One of the most important parts of this class is in the constructor, so let's take a look at it again.

internal StaThread(IQueueReader<sendorpostcallbackitem> reader)
{
   mQueueConsumer = reader;
   mStaThread = new Thread(Run);
   mStaThread.Name = "STA Worker Thread";
   mStaThread.SetApartmentState(ApartmentState.STA);
}
  • This class takes an interface of type IQueueReader, this is really our blocking queue. The reason I decided to put an interface here is because this thread is a reading thread and should not have access to writing methods.
  • The thread is being setup as an STA thread. Giving the thread a name helps when debugging using the thread output window.
  • Notice, the thread is not started yet. A method called Start will start the thread, and this will happen within our StaSynchronizationContext class, which I will show soon.

Let's take a look at the Run method. The Run method represents our STA thread. Its main job is to dequeue items from our blocking queue and execute them. Executing any work items on the Run method means executing them on the STA thread. Therefore, it doesn't really matter which thread has placed them in the queue, what's important is that items are read within the STA thread, and executed in the STA thread. If you think about this, this is, in fact, thread marshalling in action.

private void Run()
{
   while (true)
   {
      bool stop = mStopEvent.WaitOne(0);
      if (stop)
      {
         break;
      }

     SendOrPostCallbackItem workItem = mQueueConsumer.Dequeue();
     if (workItem != null)
         workItem.Execute();
   }
}

I tried to keep the Run method as simple as possible, but let's stress out a few points.

  • The STA thread is running all the time, so I have made a while(true) loop. Normally, I am not a fan of this type of loop, but I wanted the reader of the code to understand that this thread is not supposed to go down unless the context class is disposed. A while(true) sends this type of a message.
  • mStopEvent is a ManualResetEvent, it is signaled when the STA thread is marked to stop running. When the Stop() method is called, the mStopEvent is set, causing the main loop to exit. The Stop method also releases any waiting dequeue operation by marking the queue to stop processing messages.
  • mQueueConsumer.Dequeue() is responsible for reading work items from the queue. This method will block until a work item is in the queue.
  • When a work-item is dequeued, the work-item is executed. Execute(), if you remember, will execute the code in the delegate associated with the work item. It is during this Execute method that the code is marshaled on the STA thread.

Creating the STA synchronization context class

We have almost all the pieces we need to have our STA Sync Context running. We got a work item that contains our delegate to execute on the STA thread. We got a nice little blocking queue to handle communication between the STA thread and any other thread. We even have our little STA Run method always looking at our queue, pumping messages out of it, and running any work items that are fetched. The only thing we are missing now is the actual Synchronization Context class itself. So, let's see it, and go over the code in detail...

StaSynchronizationContext.cs

public class StaSynchronizationContext : SynchronizationContext, IDisposable
{
   private BlockingQueue<sendorpostcallbackitem > mQueue;
   private StaThread mStaThread;
   public StaSynchronizationContext()
      : base()
   {
      mQueue = new BlockingQueue<sendorpostcallbackitem />();
      mStaThread = new StaThread(mQueue);
      mStaThread.Start();
   }

   public override void Send(SendOrPostCallback d, object state)
   {
      // create an item for execution
      SendOrPostCallbackItem item = new SendOrPostCallbackItem(d, state, 
                                        ExecutionType.Send);
      // queue the item
      mQueue.Enqueue(item);
      // wait for the item execution to end
      item.ExecutionCompleteWaitHandle.WaitOne();

      // if there was an exception, throw it on the caller thread, not the
      // sta thread.
      if (item.ExecutedWithException)
         throw item.Exception;
   }

   public override void Post(SendOrPostCallback d, object state)
   {
      // queue the item and don't wait for its execution. This is risky because
      // an unhandled exception will terminate the STA thread. Use with caution.
      SendOrPostCallbackItem item = new SendOrPostCallbackItem(d, state, 
                                        ExecutionType.Post);
      mQueue.Enqueue(item);
   }

   public void Dispose()
   {
      mStaThread.Stop();

   }

   public override SynchronizationContext CreateCopy()
   {
      return this;
   }
}

This is really the class that uses all the other classes I have shown before. I have named it the StaSynchronizationContext because it is responsible to marshal code into an STA thread, allowing the caller to execute COM APIs that must be on an STA thread. Let's look a the Send API which is responsible for sending work on the STA thread. Notice, this class inherits from SynchronizationContext, but overrides the default Send and Post methods.

public override void Send(SendOrPostCallback d, object state)
{
   // create an item for execution
   SendOrPostCallbackItem item = 
     new SendOrPostCallbackItem(d, state, ExecutionType.Send);
   // queue the item
   mQueue.Enqueue(item);
   // wait for the item execution to end
   item.ExecutionCompleteWaitHandle.WaitOne();

   // if there was an exception, throw it on the caller thread, not the
   // sta thread.
   if (item.ExecutedWithException)
      throw item.Exception;
}

Notice that the send operation is a blocking operation, this means we block until the operation on the STA thread is complete. Remember, we have placed a ManualReset event on the SendOrPostCallbackItem class, so we know when the execution is done. We are also trapping and caching any exceptions within SendOrPostCallbackItem so we can throw them on the calling thread and not on the STA thread. The Post, on the other hand, is not a waiting call, so all we do is queue the item and we are not waiting for the delegate execution to finish.

That's it, we now have a SynchronizationContext that will marshal code between any thread into a single STA thread. In my case, this will allow me to execute COM APIs within my STA thread, so COM classes can feel at home, as if they are running in VB6. To actually test this class, I have created a test program; here is the code:

public class Params
{
  public string Output {get; set;}
  public int CallCounter { get; set; }
  public int OriginalThread { get; set; }
}

class Program
{
  private static int mCount = 0;
  private static StaSynchronizationContext mStaSyncContext = null;
  static void Main(string[] args)
  {

     mStaSyncContext = new StaSynchronizationContext();
     for (int i = 0; i < 100; i++)
     {
        ThreadPool.QueueUserWorkItem(NonStaThread);

     }
     Console.WriteLine("Processing");
     Console.WriteLine("Press any key to dispose SyncContext");
     Console.ReadLine();
     mStaSyncContext.Dispose();
  }


  private static void NonStaThread(object state)
  {
     int id = Thread.CurrentThread.ManagedThreadId;

     for (int i = 0; i < 10; i++)
     {
        var param = new Params { OriginalThread = id, CallCounter = i };
        mStaSyncContext.Send(RunOnStaThread, param);
        Debug.Assert(param.Output == "Processed", "Unexpected behavior by STA thread");
     }
  }

  private static void RunOnStaThread(object state)
  {
     mCount++;
     Console.WriteLine(mCount);
     int id = Thread.CurrentThread.ManagedThreadId;
     var args = (Params)state;
     Trace.WriteLine("STA id " + id + " original thread " + 
                     args.OriginalThread + " call count " + args.CallCounter);
     args.Output = "Processed";

  }
}

The test program will create a number of threads using the thread pool. These thread pool threads then use the StaSynchronizationContext to execute the code within the method RunOnStaThread. Running this test program, I have blasted the STA thread from 100 .NET threads, each thread marshaling the code RunOnStaThread 10 times. Notice that the STA thread ID will always be the same. Notice the results below, the STA thread is always 11, and calls are coming from multiple threads (I have shrunk the output).

STA id 11 original thread 7 call count 0
STA id 11 original thread 12 call count 0
STA id 11 original thread 7 call count 1
STA id 11 original thread 12 call count 1
STA id 11 original thread 7 call count 2
STA id 11 original thread 12 call count 2
STA id 11 original thread 7 call count 3
STA id 11 original thread 12 call count 3
STA id 11 original thread 7 call count 4
STA id 11 original thread 12 call count 4
STA id 11 original thread 7 call count 5
STA id 11 original thread 12 call count 5
STA id 11 original thread 7 call count 6
STA id 11 original thread 12 call count 6
STA id 11 original thread 7 call count 7
STA id 11 original thread 12 call count 7
STA id 11 original thread 7 call count 8
STA id 11 original thread 12 call count 8
STA id 11 original thread 7 call count 9
STA id 11 original thread 12 call count 9
STA id 11 original thread 7 call count 0
STA id 11 original thread 12 call count 0
STA id 11 original thread 7 call count 1
STA id 11 original thread 12 call count 1
STA id 11 original thread 7 call count 2
STA id 11 original thread 12 call count 2
STA id 11 original thread 7 call count 3
STA id 11 original thread 12 call count 3
STA id 11 original thread 7 call count 4
STA id 11 original thread 12 call count 4
STA id 11 original thread 7 call count 5
STA id 11 original thread 12 call count 5
STA id 11 original thread 7 call count 6
STA id 11 original thread 12 call count 6
STA id 11 original thread 7 call count 7
STA id 11 original thread 12 call count 7
STA id 11 original thread 7 call count 8
STA id 11 original thread 12 call count 8
STA id 11 original thread 7 call count 9
STA id 11 original thread 12 call count 9
STA id 11 original thread 7 call count 0
STA id 11 original thread 12 call count 0
STA id 11 original thread 7 call count 1
STA id 11 original thread 12 call count 1
STA id 11 original thread 7 call count 2
STA id 11 original thread 12 call count 2
STA id 11 original thread 7 call count 3
STA id 11 original thread 12 call count 3
STA id 11 original thread 7 call count 4
STA id 11 original thread 12 call count 4
STA id 11 original thread 7 call count 5
STA id 11 original thread 12 call count 5
STA id 11 original thread 7 call count 6
STA id 11 original thread 12 call count 6

Wait a minute, I can do the same thing without using a SynchronizationContext, so why bother?

True, there is really no need to build a synchronization context to marshal code into another thread. I could just use my queue and the STA thread to do all the work directly within my code. It's nice that I am playing by the rules of .NET, and I am providing my own implementation, so others can use it, but it is not really required. So, why bother? The reason is because of WCF. WCF allows you to provide a SynchronizationContext for the execution of operations within a service. This is a very powerful feature of WCF that is normally used to marshal WCF service calls into the UI thread. However, in my case, because I played nice and I created a SynchronizationContext class and not just any class, I can now tell WCF to execute all my service methods on an STA thread. This was the main reason for writing the series on this class.

Conclusion

This article shows that you can create your own version of a the SynchronizationContext class. I have shown that a Blocking Queue is a good candidate for communication between threads. We successfully marshaled code from any thread into a single STA thread. Now that we have our Synchronization Context class, we can put it to work within a WCF service. This will be the main focus of the third part of the series. Once you understand how to build your own Synchronization Context and apply it to a WCF service, you are in control of "where" your code is running when a WCF method is executed - a very powerful feature within a very powerful WCF framework.

For those of you that want the code, I will provide a full VS2008 solution in the third part of this article series.

Sunday, December 21, 2008

Understanding SyncrhronizationContext

SynchronizationContext - MSDN Lets You Down

I don't know why, but there is really not much information about this new class within the .NET Framework. The MSDN documentation contains very little information on how to use SynchronizationContext. Initially, I must say that I had a hard time understanding the reason for this new class and how to use it. After reading a lot on the subject, I finally understood the purpose of this class and how it should be used. I decided to write this article to help other developers understand how to use this class, and what it can and cannot do for you. (MSDN)

Using SynchronizationContext to Marshal Code from One Thread to Another

Let's get some technical points out of the way so we can show how to use this class. A SynchronizationContext allows a thread to communicate with another thread. Suppose you have two threads, Thread1 and Thread2. Say, Thread1 is doing some work, and then Thread1 wishes to execute code on Thread2. One possible way to do it is to ask Thread2 for its SynchronizationContext object, give it to Thread1, and then Thread1 can call SynchronizationContext.Send to execute the code on Thread2. Sounds like magic... Well, there is a something you should know. Not every thread has a SynchronizationContext attached to it. One thread that always has a SynchronizationContext is the UI thread.

Who puts the SynchronizationContext into the UI thread? Any guesses? Give up? OK, here it is, the first control that is created on the thread places the SynchronizationContext into that thread. Normally, it is the first form that gets created. How do I know? Well, I tried it out.

Because my code uses SynchronizationContext.Current, let me explain what this static property gives us. SynchronizationContext.Current allows us to get a SynchronizationContext that is attached to the current thread. Let's be clear here, SynchronizationContext.Current is not a singleton per AppDomain, but per thread. This means that two threads can have different instances of SynchronizationContext when calling SynchronizationContext.Current. If you wonder where the actual context is stored, it is stored within the Thread data store (and as I said before, not in the global memory space of the appdomain).

OK, let's look at the code that places a SynchronizationContext within our UI thread:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    // let's check the context here
    var context = SynchronizationContext.Current;
    if (context == null)
        MessageBox.Show("No context for this thread");
    else
        MessageBox.Show("We got a context");

    // create a form
    Form1 form = new Form1();

    // let's check it again after creating a form
    context = SynchronizationContext.Current;

    if (context == null)
        MessageBox.Show("No context for this thread");
    else
        MessageBox.Show("We got a context");

    if (context == null)
        MessageBox.Show("No context for this thread");

    Application.Run(new Form1());
}

As you can see, there are a few points to note:

  • The first message box will indicate that there is no context attached to the thread. That's because .NET doesn't even know what is going to happen on this thread, and there is no runtime class that initializes the Sync Context for this thread.
  • Right after creating the form, notice that the context is set. The Form class is responsible for this. It checks if a Sync Context is present, and if it is not, it places it there. Remember that the context is always the same on the same thread, so any UI control can access it. This is because all UI operations must be running on the UI thread. To be more specific, the thread that creates the window is the thread that can communicate with the window. In our case, it is the main thread of the application.

How Do I Use It?

Now that the UI thread is nice enough to give us a Sync Context so we can "run code" under the UI thread, how do we use it?

First, do we really have to marshal code into the UI thread? Yes. If you are running on a thread other than the UI thread, you cannot update the UI. Want to be a hero and try it? You will get an exception (in version 1.0, they didn't enforce the exception, it just crashed the application, but in version 2.0, there is a fat ugly exception that pops in your face).

To be fair, I will say that you don't have to use this class to sync into the UI thread. You can use the InvokeRequired property (which is on every UI control class) and see if you need to marshal your code. If you get a "true" out of InvokeRequired, then you have to use Control.Invoke to marshal the code to the UI thread. Great! Why keep reading? Well, there is an issue with this technique. You must have a Control in order to call Invoke on it. It doesn't matter which UI control, but you need at least one control reference available to you within your non-UI thread in order to do this type of thread marshalling. From a design prospective, you never want to have a UI reference within your BI layer. So, you can leave all sync operations on the UI class, and make sure the UI is responsible to marshal its own work (see my article on the MVP pattern). However, this puts more responsibility on the UI, and makes the UI smarter than we want it to be, I must say. It would be nice for the BI to have the ability to marshal code to the UI thread without having a reference to a control or a form.

So, how is it done?

Simple. Create a thread, send it the sync context, and have this thread use the sync object to marshal code into the UI thread. Let's see an example.

In the following example, I have a list box that is populated from a worker thread. The thread simulates a computation and then writes to the UI list box. The thread used to update the UI is launched from the mToolStripButtonThreads_Click event handler.

First, let's see what's on the form:

    private void InitializeComponent()
    {
        System.ComponentModel.ComponentResourceManager resources =
          new System.ComponentModel.ComponentResourceManager(typeof(Form1));
        this.mListBox = new System.Windows.Forms.ListBox();
        this.toolStrip1 = new System.Windows.Forms.ToolStrip();
        this.mToolStripButtonThreads = new System.Windows.Forms.ToolStripButton();
        this.toolStrip1.SuspendLayout();
        this.SuspendLayout();
        //
        // mListBox
        //
        this.mListBox.Dock = System.Windows.Forms.DockStyle.Fill;
        this.mListBox.FormattingEnabled = true;
        this.mListBox.Location = new System.Drawing.Point(0, 0);
        this.mListBox.Name = "mListBox";
        this.mListBox.Size = new System.Drawing.Size(284, 264);
        this.mListBox.TabIndex = 0;
        //
        // toolStrip1
        //
        this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
        this.mToolStripButtonThreads});
        this.toolStrip1.Location = new System.Drawing.Point(0, 0);
        this.toolStrip1.Name = "toolStrip1";
        this.toolStrip1.Size = new System.Drawing.Size(284, 25);
        this.toolStrip1.TabIndex = 1;
        this.toolStrip1.Text = "toolStrip1";
        //
        // mToolStripButtonThreads
        //
        this.mToolStripButtonThreads.DisplayStyle =
          System.Windows.Forms.ToolStripItemDisplayStyle.Text;
        this.mToolStripButtonThreads.Image = ((System.Drawing.Image)
            (resources.GetObject("mToolStripButtonThreads.Image")));
        this.mToolStripButtonThreads.ImageTransparentColor =
             System.Drawing.Color.Magenta;
        this.mToolStripButtonThreads.Name = "mToolStripButtonThreads";
        this.mToolStripButtonThreads.Size = new System.Drawing.Size(148, 22);
        this.mToolStripButtonThreads.Text = "Press Here to start threads";
        this.mToolStripButtonThreads.Click +=
          new System.EventHandler(this.mToolStripButtonThreads_Click);
        //
        // Form1
        //
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(284, 264);
        this.Controls.Add(this.toolStrip1);
        this.Controls.Add(this.mListBox);
        this.Name = "Form1";
        this.Text = "Form1";
        this.toolStrip1.ResumeLayout(false);
        this.toolStrip1.PerformLayout();
        this.ResumeLayout(false);
        this.PerformLayout();
    }

    #endregion

    private System.Windows.Forms.ListBox mListBox;
    private System.Windows.Forms.ToolStrip toolStrip1;
    private System.Windows.Forms.ToolStripButton mToolStripButtonThreads;
}

Now, let's see the example:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void mToolStripButtonThreads_Click(object sender, EventArgs e)
    {
        // let's see the thread id
        int id = Thread.CurrentThread.ManagedThreadId;
        Trace.WriteLine("mToolStripButtonThreads_Click thread: " + id);

        // grab the sync context associated to this
        // thread (the UI thread), and save it in uiContext
        // note that this context is set by the UI thread
        // during Form creation (outside of your control)
        // also note, that not every thread has a sync context attached to it.
        SynchronizationContext uiContext = SynchronizationContext.Current;

        // create a thread and associate it to the run method
        Thread thread = new Thread(Run);

        // start the thread, and pass it the UI context,
        // so this thread will be able to update the UI
        // from within the thread
        thread.Start(uiContext);
    }

    private void Run(object state)
    {
        // lets see the thread id
        int id = Thread.CurrentThread.ManagedThreadId;
        Trace.WriteLine("Run thread: " + id);

        // grab the context from the state
        SynchronizationContext uiContext = state as SynchronizationContext;

        for (int i = 0; i < 1000; i++)
        {
            // normally you would do some code here
            // to grab items from the database. or some long
            // computation
            Thread.Sleep(10);

            // use the ui context to execute the UpdateUI method,
            // this insure that the UpdateUI method will run on the UI thread.

            uiContext.Post(UpdateUI, "line " + i.ToString());
        }
    }

    /// <summary>
    /// This method is executed on the main UI thread.
    /// </summary>
    private void UpdateUI(object state)
    {
        int id = Thread.CurrentThread.ManagedThreadId;
        Trace.WriteLine("UpdateUI thread:" + id);
        string text = state as string;
        mListBox.Items.Add(text);
    }
}

Let's go over this code. Notice that I log the thread ID of each method so we can review it later.

For example:

// let's see the thread id
int id = Thread.CurrentThread.ManagedThreadId;
Trace.WriteLine("mToolStripButtonThreads_Click thread: " + id);

When pressing on the toolstrip button, a thread is launched with its delegate pointing to the Run method. However, notice that I am passing state to this thread. I am passing the Sync Context of the UI thread by calling:

SynchronizationContext uiContext = SynchronizationContext.Current;

Because I am running on the event handler thread of the toolstrip button, I know I am currently running on the UI thread, and by calling SynchronizationContext.Current, I will get the sync context for the UI thread.

Run will first grab the SynchronizationContext from its state, so it can have the knowledge of how to marshal code into the UI thread.

// grab the context from the state
SynchronizationContext uiContext = state as SynchronizationContext;

The Run thread writes 1000 lines into the list box. How? Well, first it uses the Send method on the SynchronizationContext:

public virtual void Send(SendOrPostCallback d, object state);

Calling SynchronizationContext.Send takes two arguments, a delegate pointing to a method and a state object. Within our example...

uiContext.Send(UpdateUI, "line " + i.ToString());

... UpdateUI is the value we provide for the delegate, and state contains the string we want to add to the listbox. The code in UpdateUI is supposed to run on the UI thread, and not on the calling thread.

private void UpdateUI(object state)
{
    int id = Thread.CurrentThread.ManagedThreadId;
    Trace.WriteLine("UpdateUI thread:" + id);
    string text = state as string;
    mListBox.Items.Add(text);
}

Notice that this code directly runs on the UI thread. There is no checking for InvokerRequired because I know it is on the UI thread due to the fact that it was used with the Send method of the UI SynchronizationContext.

Let's look at the thread IDs and see if it makes sense:

mToolStripButtonThreads_Click thread: 10
Run thread: 3
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
UpdateUI thread:10
... (x1000 times)

This means that the UI thread is 10, the worker thread (Run) is 3, and when we update the UI, notice we are on thread ID 10 again (the UI thread). So, everything is working as advertised.

Error Handling

Very nice, we are able to marshal code into the UI thread, but what happens when the code we marshal throws an exception? Who is responsible to catch it? The UI thread or the worker thread?

private void Run(object state)
{
    // let's see the thread id
    int id = Thread.CurrentThread.ManagedThreadId;
    Trace.WriteLine("Run thread: " + id);

    // grab the context from the state
    SynchronizationContext uiContext = state as SynchronizationContext;

    for (int i = 0; i < 1000; i++)
    {
        Trace.WriteLine("Loop " + i.ToString());
        // normally you would do some code here
        // to grab items from the database. or some long
        // computation
        Thread.Sleep(10);

        // use the ui context to execute the UpdateUI method, this insure that the
        // UpdateUI method will run on the UI thread.

        try
        {
            uiContext.Send(UpdateUI, "line " + i.ToString());
        }
        catch (Exception e)
        {
            Trace.WriteLine(e.Message);
        }
    }
}

/// <summary>
/// This method is executed on the main UI thread.
/// </summary>
private void UpdateUI(object state)
{
    throw new Exception("Boom");
}

I modified the code so that the UpdateUI method throws an exception:

throw new Exception("Boom");

Also, I have modified the Run method to place a try/catch around the Send method.

try
{
    uiContext.Send(UpdateUI, "line " + i.ToString());
}
catch (Exception e)
{
    Trace.WriteLine(e.Message);
}

When running this code, I noticed that the exception is caught in the Run thread and not on the UI thread. This is interesting because you might expect the exception to bring down the UI thread, considering no class is catching the exception on the UI thread.

Therefore, the Send method is doing a little magic; it is executing our code in a blocking fashion, and it reports back any exception during its execution.

Send vs. Post

Using Send is only one of two possible methods you can use to marshal code on the UI thread. There is another method called Post. What's the difference? A lot!

Maybe it is time to see this class in more detail, so let's review the interface of SynchronizationContext:

// Summary:
//     Provides the basic functionality for propagating a synchronization context
//     in various synchronization models.
public class SynchronizationContext
{
    // Summary:
    //     Creates a new instance of the System.Threading.SynchronizationContext class.
    public SynchronizationContext();

    // Summary:
    //     Gets the synchronization context for the current thread.
    //
    // Returns:
    //     A System.Threading.SynchronizationContext object representing the current
    //     synchronization context.
    public static SynchronizationContext Current { get; }

    // Summary:
    //     When overridden in a derived class, creates a copy of the synchronization
    //     context.
    //
    // Returns:
    //     A new System.Threading.SynchronizationContext object.
    public virtual SynchronizationContext CreateCopy();
    //
    // Summary:
    //     Determines if wait notification is required.
    //
    // Returns:
    //     true if wait notification is required; otherwise, false.
    public bool IsWaitNotificationRequired();
    //
    // Summary:
    //     When overridden in a derived class, responds to the notification that an
    //     operation has completed.
    public virtual void OperationCompleted();
    //
    // Summary:
    //     When overridden in a derived class, responds to the notification that an
    //     operation has started.
    public virtual void OperationStarted();
    //
    // Summary:
    //     When overridden in a derived class, dispatches an asynchronous message to
    //     a synchronization context.
    //
    // Parameters:
    //   d:
    //     The System.Threading.SendOrPostCallback delegate to call.
    //
    //   state:
    //     The object passed to the delegate.
    public virtual void Post(SendOrPostCallback d, object state);
    //
    // Summary:
    //     When overridden in a derived class, dispatches a synchronous message to a
    //     synchronization context.
    //
    // Parameters:
    //   d:
    //     The System.Threading.SendOrPostCallback delegate to call.
    //
    //   state:
    //     The object passed to the delegate.
    public virtual void Send(SendOrPostCallback d, object state);
    //
    // Summary:
    //     Sets the current synchronization context.
    //
    // Parameters:
    //   syncContext:
    //     The System.Threading.SynchronizationContext object to be set.
    public static void SetSynchronizationContext(SynchronizationContext syncContext);
    //
    // Summary:
    //     Sets notification that wait notification is required and prepares the callback
    //     method so it can be called more reliably when a wait occurs.
    protected void SetWaitNotificationRequired();
    //
    // Summary:
    //     Waits for any or all the elements in the specified array to receive a signal.
    //
    // Parameters:
    //   waitHandles:
    //     An array of type System.IntPtr that contains the native operating system
    //     handles.
    //
    //   waitAll:
    //     true to wait for all handles; false to wait for any handle.
    //
    //   millisecondsTimeout:
    //     The number of milliseconds to wait, or System.Threading.Timeout.Infinite
    //     (-1) to wait indefinitely.
    //
    // Returns:
    //     The array index of the object that satisfied the wait.
    [PrePrepareMethod]
    [CLSCompliant(false)]
    public virtual int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout);
    //
    // Summary:
    //     Helper function that waits for any or all the elements in the specified array
    //     to receive a signal.
    //
    // Parameters:
    //   waitHandles:
    //     An array of type System.IntPtr that contains the native operating system
    //     handles.
    //
    //   waitAll:
    //     true to wait for all handles; false to wait for any handle.
    //
    //   millisecondsTimeout:
    //     The number of milliseconds to wait, or System.Threading.Timeout.Infinite
    //     (-1) to wait indefinitely.
    //
    // Returns:
    //     The array index of the object that satisfied the wait.
    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
    [PrePrepareMethod]
    [CLSCompliant(false)]
    protected static int WaitHelper(IntPtr[] waitHandles,
                     bool waitAll, int millisecondsTimeout);
}

Notice the comment for the Post method:

//
// Summary:
//     When overridden in a derived class, dispatches an asynchronous message to
//     a synchronization context.
//
// Parameters:
//   d:
//     The System.Threading.SendOrPostCallback delegate to call.
//
//   state:
//     The object passed to the delegate.
public virtual void Post(SendOrPostCallback d, object state);

The key word here is asynchronous. This means that Post will not wait for the execution of the delegate to complete. Post will "Fire and Forget" about the execution code within the delegate. It also means that you cannot catch exceptions as we did with the Send method. Suppose an exception is thrown, it will be the UI thread that will get it; unhanding the exception will terminate the UI thread.

However, Post or Send, the execution of the delegate always runs on the correct thread. Just replace the code with Post instead of Send, and you will still get the right thread ID when executing on the UI thread.

So Now, I Can Use SynchronizationContext to Sync Any Thread I Want, Right? Nope!

At this point, you might try to use SynchronizationContext with any thread. However, you will soon find that your thread does not have a SynchronizationContext when using SynchronizationContext.Current, and it always returns null. No big deal you say, and you simply create a SynchronizationContext if there isn't one. Simple. But, it does not really work.

Let's look at a program similar to the one we used for the UI thread:

class Program
{
    private static SynchronizationContext mT1 = null;

    static void Main(string[] args)
    {
        // log the thread id
        int id = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine("Main thread is " + id);

        // create a sync context for this thread
        var context = new SynchronizationContext();
        // set this context for this thread.
        SynchronizationContext.SetSynchronizationContext(context);

        // create a thread, and pass it the main sync context.
        Thread t1 = new Thread(new ParameterizedThreadStart(Run1));
        t1.Start(SynchronizationContext.Current);
        Console.ReadLine();
    }

    static private void Run1(object state)
    {
        int id = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine("Run1 Thread ID: " + id);

        // grab  the sync context that main has set
        var context = state as SynchronizationContext;

        // call the sync context of main, expecting
        // the following code to run on the main thread
        // but it will not.
        context.Send(DoWork, null);

        while (true)
            Thread.Sleep(10000000);
    }

    static void DoWork(object state)
    {
        int id = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine("DoWork Thread ID:" + id);
    }
}

This simple console application is something you should not do at home. This program does not work, and it is done simply to prove a point. Notice I am setting a Sync Context on the main console thread. I am simply creating a new instance of the object. Then, I am setting it to my current thread. This is similar to what the UI thread does when a form is created (not really, but I will explain later). Then, I create a thread Run1, and pass it the context of the main thread. When I try to call Send, based on my trace, I notice Send was called on the Run1 thread and not on the main thread as we may expect. Here is the output:

Main thread is 10
Run1 Thread ID: 11
DoWork Thread ID:11

Notice that DoWork is executed on thread 11, the same thread as Run1. Not much of a SynchronizationContext into the main thread. Why? What's going on? Well... This is the part when you realize that nothing is for free in life. Threads can't just switch contexts between them, they must have an infrastructure built-in into them in order to do so. The UI thread, for example, uses a message pump, and within its SynchronizationContext, it leverages the message pump to sync into the UI thread.

So, the UI thread has it own SynchronizationContext class, but it is a class that derives from SynchronizationContext, and it is called System.Windows.Forms.WindowsFormsSynchronizationContext. Now, this class has a very different implementation from the simple plain vanilla SynchronizationContext. The UI version overrides the Post and Send methods, and provides a "message pump" version of these methods (I tried to get the source code of this class, but I didn't find it). So, what does the plain vanilla SynchronizationContext do?

Somehow, I was able to get the source code of SynchronizationContext, and here it is: found it here.

(I have removed the attributes, and did some minor formatting just to have the code fit in the page.)

namespace System.Threading
{
    using Microsoft.Win32.SafeHandles;
    using System.Security.Permissions;
    using System.Runtime.InteropServices;
    using System.Runtime.CompilerServices;
    using System.Runtime.ConstrainedExecution;
    using System.Reflection;

    internal struct SynchronizationContextSwitcher : IDisposable
    {
        internal SynchronizationContext savedSC;
        internal SynchronizationContext currSC;
        internal ExecutionContext _ec;

        public override bool Equals(Object obj)
        {
            if (obj == null || !(obj is SynchronizationContextSwitcher))
                return false;
            SynchronizationContextSwitcher sw = (SynchronizationContextSwitcher)obj;
            return (this.savedSC == sw.savedSC &&
                    this.currSC == sw.currSC && this._ec == sw._ec);
        }

        public override int GetHashCode()
        {
            return ToString().GetHashCode();
        }

        public static bool operator ==(SynchronizationContextSwitcher c1,
                                       SynchronizationContextSwitcher c2)
        {
            return c1.Equals(c2);
        }

        public static bool operator !=(SynchronizationContextSwitcher c1,
                                       SynchronizationContextSwitcher c2)
        {
            return !c1.Equals(c2);
        }

        void IDisposable.Dispose()
        {
            Undo();
        }

        internal bool UndoNoThrow()
        {
            if (_ec  == null)
            {
                return true;
            }

            try
            {
                Undo();
            }
            catch
            {
                return false;
            }
            return true;
        }

        public void Undo()
        {
            if (_ec  == null)
            {
                return;
            }

            ExecutionContext  executionContext =
              Thread.CurrentThread.GetExecutionContextNoCreate();
            if (_ec != executionContext)
            {
                throw new InvalidOperationException(Environment.GetResourceString(
                          "InvalidOperation_SwitcherCtxMismatch"));
            }
            if (currSC != _ec.SynchronizationContext)
            {
                throw new InvalidOperationException(Environment.GetResourceString(
                          "InvalidOperation_SwitcherCtxMismatch"));
            }
            BCLDebug.Assert(executionContext != null, " ExecutionContext can't be null");
            // restore the Saved Sync context as current
            executionContext.SynchronizationContext = savedSC;
            // can't reuse this anymore
            _ec = null;
        }
    }

    public delegate void SendOrPostCallback(Object state);

    [Flags]
    enum SynchronizationContextProperties
    {
        None = 0,
        RequireWaitNotification = 0x1
    };

    public class SynchronizationContext
    {
        SynchronizationContextProperties _props = SynchronizationContextProperties.None;

        public SynchronizationContext()
        {
        }

        // protected so that only the derived sync
        // context class can enable these flags
        protected void SetWaitNotificationRequired()
        {
            // Prepare the method so that it can be called
            // in a reliable fashion when a wait is needed.
            // This will obviously only make the Wait reliable
            // if the Wait method is itself reliable. The only thing
            // preparing the method here does is to ensure there
            // is no failure point before the method execution begins.

            RuntimeHelpers.PrepareDelegate(new WaitDelegate(this.Wait));
            _props |= SynchronizationContextProperties.RequireWaitNotification;
        }

        public bool IsWaitNotificationRequired()
        {
            return ((_props &
              SynchronizationContextProperties.RequireWaitNotification) != 0);
        }

        public virtual void Send(SendOrPostCallback d, Object state)
        {
            d(state);
        }

        public virtual void Post(SendOrPostCallback d, Object state)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
        }

        public virtual void OperationStarted()
        {
        }

        public virtual void OperationCompleted()
        {
        }

        // Method called when the CLR does a wait operation
        public virtual int Wait(IntPtr[] waitHandles,
                       bool waitAll, int millisecondsTimeout)
        {
            return WaitHelper(waitHandles, waitAll, millisecondsTimeout);
        }

        // Static helper to which the above method
        // can delegate to in order to get the default
        // COM behavior.
        protected static extern int WaitHelper(IntPtr[] waitHandles,
                         bool waitAll, int millisecondsTimeout);

        // set SynchronizationContext on the current thread
        public static void SetSynchronizationContext(SynchronizationContext syncContext)
        {
            SetSynchronizationContext(syncContext,
              Thread.CurrentThread.ExecutionContext.SynchronizationContext);
        }

        internal static SynchronizationContextSwitcher
          SetSynchronizationContext(SynchronizationContext syncContext,
          SynchronizationContext prevSyncContext)
        {
            // get current execution context
            ExecutionContext ec = Thread.CurrentThread.ExecutionContext;
            // create a switcher
            SynchronizationContextSwitcher scsw = new SynchronizationContextSwitcher();

            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                // attach the switcher to the exec context
                scsw._ec = ec;
                // save the current sync context using the passed in value
                scsw.savedSC = prevSyncContext;
                // save the new sync context also
                scsw.currSC = syncContext;
                // update the current sync context to the new context
                ec.SynchronizationContext = syncContext;
            }
            catch
            {
                // Any exception means we just restore the old SyncCtx
                scsw.UndoNoThrow(); //No exception will be thrown in this Undo()
                throw;
            }
            // return switcher
            return scsw;
        }

        // Get the current SynchronizationContext on the current thread
        public static SynchronizationContext Current
        {
            get
            {
                ExecutionContext ec = Thread.CurrentThread.GetExecutionContextNoCreate();
                if (ec != null)
                    return ec.SynchronizationContext;
                return null;
            }
        }

        // helper to Clone this SynchronizationContext,
        public virtual SynchronizationContext CreateCopy()
        {
            // the CLR dummy has an empty clone function - no member data
            return new SynchronizationContext();
        }

        private static int InvokeWaitMethodHelper(SynchronizationContext syncContext,
            IntPtr[] waitHandles,
            bool waitAll,
            int millisecondsTimeout)
        {
            return syncContext.Wait(waitHandles, waitAll, millisecondsTimeout);
        }
    }
}

Look at the implementation of Send and Post...

public virtual void Send(SendOrPostCallback d, Object state)
{
    d(state);
}

public virtual void Post(SendOrPostCallback d, Object state)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
}

Send simply calls the delegate on the calling thread (no thread switching of any kind), and Post does the same thing, but simply uses the ThreadPool to do it in an async fashion. In my opinion, this class should be abstract. The default implementation of this class is confusing and useless. It is one of two reasons I decided to write this article.

Conclusion

I hope you know more about this class now, and you understand how to use it. Within .NET, I found two classes that provide a custom synchronization. One for the WinForms thread context and one for WPF thread context. I am sure there are more, but these are the ones I found so far. The default implementation of the class, as I showed you, does nothing to switch code from one thread to another. This is simply because threads, by default, do not have this type of mechanism. UI threads, on the other hand, have a message pump and Windows APIs such as SendMessage and PostMessage that I am sure are used when marshalling code to the UI thread.

However, this should not be the end of the road for this class. You can make your own SynchronizationContext, it is really simple. In fact, I had to write one. At my work, we needed to have all COM based calls executed on an STA thread. However, our application is using the thread pool and WCF, and it was not simple to just marshal code into an STA thread. Therefore, I decided to code my own version of SynchronizationContext called StaSynchronizationContext. I will show how I did it in part II of this article.

Happy .NETting.