My team is developing a multi-threaded application using async/await in C# 5.0. In the process of implementing thread synchronization, after several iterations, we came up with a (possibly novel?) new SynchronizationContext implementation with an internal lock that:
- When calling Post:
- if a lock can be taken, the delegate is executed immediately
- if a lock cannot be taken, the delegate is queued
- When calling Send:
- if a lock can be taken the delegate is executed
- if a lock cannot be taken, the thread is blocked
In all cases, before executing the delegate, the context sets itself as the current context and restores the original context when the delegate returns.
It’s an unusual pattern and since we’re clearly not the first people writing such an application I’m wondering:
- Is the pattern really safe?
- Are there better ways of achieving thread synchronization?
Here’s the source for SerializingSynchronizationContext and a demo on GitHub.
Here’s how it’s used:
- Each class wanting protection creates its own instance of the context like a mutex.
The context is awaitable so that statements like the following are possible.
await myContext;
This simply causes the rest of the method to be run under protection of the context.
- All methods and properties of the class use this pattern to protect data. Between awaits, only one thread can run on the context at a time and so state will remain consistent. When an await is reached, the next scheduled thread is allowed to run on the context.
- The custom SynchronizationContext can be used in combination with AsyncLock if needed to maintain atomicity even with awaited expressions.
- Synchronous methods of a class can use custom context for protection as well.
ActionBlock
a little? – HarkeyWinFormsSynchronizationContext
which always restores the original thread, on something likeAspNetSynchronizationContext
, which still serializes execution, but nevertheless allows a thread switch (so it scales better)? Also, what's the goal for this way of synchronizing, are you objects not thread-safe by design? I can't help myself linking my own question here. – Zilpah