Keeping Threads Untangled (Server-Side)Posted: 2012-03-22
WCF, WPF, and TPL. Three programming APIs with thread issues. Getting them to work together across multiple application domains with shared state and callback has its challenges.
First of all, the framework currently is spread across three services, each with its own WCF CallbackContract: LoginService, VisualizationService and IkosaService. By using callback contracts, I skirt around forcing clients to ping-poll to see if anything new is ready. I also went with Net.TCP connections, allowing duplex channels without having to muck about with port forwarding on home firewalls.
Currently, I work under a known service pattern, with each service at its own fixed endpoint servicing all requests. Therefore, I have a [ServiceBehavior] attribute that specified ConcurrencyMode.Multiple, InstanceContextMode.Single, and UseSynchronizationContext=false. I made the class use static members, limiting me to one per application domain (may change this in the future, but not pressed to do it at the moment).
What these ServiceBehaviors do:
– ConcurrencyMode.Multiple: multiple threads are allowed in, and I handle thread synchronization
– InstanceContextMode.Single: basically means the service is a shared singleton
– UseSynchronizationContext=false: if I didn’t do this, inbound processing requests would get queued into the main UI’s thread, causing contention with the UI
These concurrent, unsynchronized singleton services need strategies to handle read and update collisions of their shared state. I have tended to favor ReaderWriterLockSlim in the past, but have also delved into TPL concurrent collections for some of the basic list management and notification queues. This works well with classifying service calls as lock-free, requires-read-lock, or write-blocking (and the occasional upgradeable-read-lock).
When probing the shared Ikosa state (or packaged resources) I shift into ReaderWriterLockSlim mode; when dealing with pure client-server stuff (such as managing the list of potential callback candidates), I tend to rely on the concurrent collections like the ConcurrentDictionary, and get to run without locking the ReaderWriterLockSlim stuff.
One last thing on the server-side is providing feedback from the largely free-threaded servicing hosts back to the host UI. Since calls to WPF UI needs to be dispatched, I provide an Action delegate to each service they can use to call-back. When I assign the delegate in the host, I need to use a Dispatcher.Invoke(), but I also need to wrap that in an Task.Factory.StartNew(), so that the feedback doesn’t get blocked by whatever the UI is doing, and can serviced when the UI thread gets around to servicing messages again.
// TODO: do stuff with msg
Well, next I’ll give a brief view of the client threading considerations.