Menu

Threadsafety and SynchronizationContext

asy proger

The reponse callback to a asynchronous SendOut() operation should be routed to the same thread as the send was called from.
Otherwise send and receive operations of an actor do not run on the same thread.
Accessing your data from different threads leads to race conditions - very difficult to find programming errors.
The recommended cure are locks, but they may lead to deadlocks - even more difficult to find programming errors!
We want lockfree code and therefore accept only a single thread for an actor.

This is possible as long as the SendOut() was made from a Microsoft.Windows.Forms- or a WPF class. These objects are created from a thread that has a "System.Threading.SynchronizationContext". SynchronizationContext is the base class for the implementation of a message queue. Notably it contains the asynchronous Post() method.

There is a very fine implementation of "System.Threading.SynchronizationContext" in Nito.Async.ActionThread (http://nitoasync.codeplex.com).
It perfectly fits AsyncWcfLib and is used e.g. in Test1.Client. ActionThread has a message queue and a dispatcher. The library also contains a matching timer class.

You might think that async-await is another solution for lockfree code.
This is only true in the presence of a "System.Threading.SynchronizationContext" (like in Nito.Async.ActionThread, WinForms or WPF).
Otherwise your continuation code after await runs on a threadpool-thread and you are accessing the state of your actor from two threads (maybe from two CPU's) possibly at the same time.

The .NET framework defaults to the so called "free-threaded" context (System.Threading.SynchronizationContext.Current == null). It just uses another threadpool thread after an await or for a response callback event.
Free-threading is used in:

  • Console applications
  • Windows services (applications without graphical user interface)
  • Send operations initiated by a threadpool thread (e.g. started by a System.Threading.Timer)
  • Send operations initiated by a user created thread not having a synchronization context.
  • await Task.Run(delegate) - runs 'delegate' on a threadpool thread. Make sure 'delegate' does not access your data.
  • await task.ConfigureAwait(false) - ignors the presence of a synchronization context and completes on a threadpool thread.

There is more information in


Related

Wiki: Async-Await and Actor rules!
Wiki: Asynchronous Responses
Wiki: Home
Wiki: Implement a client using AsyncWcfLib
Wiki: Run AsyncWcfLib.Test1

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.