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.

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:

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 PerSessionInstanceContextMode 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.

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.

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.

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:

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.

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:

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:

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.

Share

About the Author

I am currently working as a team leader with a group of amazing .NET programmers. I love coding with .NET, and I love to apply design patterns into my work. Lately I had some free time, so I decided to write some articles, hoping I will spare someone frustration and anxiety.

I have a WCF service, running in IIS.
The service method is wrapped in STA Operation Behavior (from devlicio.us), but it lacked the ability to get it running on the main thread, this is for rendering UIElements and I am looking for an Application.Current.Dispatcher alternative
(to avoid the exception Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.)
This article gave me hope but the service I tested was not running on the main thread, am I missing something?

I sell software programs in infomercials on national television in the United States and there is a BIG demand from TV viewers to buy Stock Alert Software. This is software that delivers stock data based upon over 1,000 different parameters used to predict which stocks will go up or down.

These programs typically run off COMET AJAX servers because they MUST delivers about 2,000 rows of alert data PER SECOND into a web page--yes, that is correct.

My question is this: Is the best design for a WCF Server that will be a Stock Alert Server "PerCall" or "PerSession"?

When a web page connects to the WCF Server, a thread is spun off that opens a TCP connection to Data Server and that TCP connection must be held open and the Data Server will push data in the form of a callback to the WCF Server, which, in turn, will return it to the browser that sent in the configuration string for those alerts.

The idea is to "spin off" a new instance of the WCF Server per connection by a client browser--each web page MUST see ONLY the data it requested and no 2 configuration as the same because each configuration string has over 1,000 charaters specifying complex parameters.

What is the best type of setup for a WCF Server to do this? Subscription will NOT work because there is too much data.

You can't buy these programs--they are licensed and the fee is $300 per month per customer--and the demand for this is growing very fast.

Feel free to email me if you would like to know more at tvmogul1@yahoo.com

using winforms with WCF is as easy as ensuring that SynchronizationContext.Current is set before initializing the WCF Service Host. This is as easy as calling new Form() before creating the WCF ServiceHost.

Yes you are right, but this is only if you are hosting the service in a winform. If you want to host your service in IIS/WAS or even a Windows Service your method will not work. In fact, hosting in winform is probably the worst type of hosting.

Thanks for your feedback.

"There are only 10 types of people in the world: Those who understand binary, and those who don't"More articles on my blog