Concurrency in .NET: which tool when
A one-page map of .NET's concurrency tools, with the bugs each one prevents.
Concurrency in .NET: which tool when
When several threads touch the same data, three bugs appear: race conditions (lost updates), deadlocks (threads waiting on each other forever) and starvation (some work never gets a turn). Pick the tool that fits the situation.
| Situation | Tool | Why |
|---|---|---|
| Add to a shared counter | Interlocked |
atomic, no lock needed |
| Protect a few lines of sync code | lock (on a private object, or Lock in .NET 9+) |
simple, fast; no await inside |
Protect code that uses await |
SemaphoreSlim(1, 1) |
works with WaitAsync |
| Limit how many run at once | SemaphoreSlim(n, n) |
lets n callers in |
| One lock across processes | named Mutex |
visible to other processes |
| Many readers, few writers | ReaderWriterLockSlim |
readers don’t block each other; not for async code |
| Shared dictionary or queue | ConcurrentDictionary, ConcurrentQueue |
thread-safe operations |
| Hand work from producers to consumers | Channel<T> |
async queue; a bounded one makes fast producers wait |
| Wait for I/O | async/await |
no thread held while waiting |
Rules that prevent most bugs
- Share as little as possible. Immutable data needs no lock.
- Keep locked blocks short, and never call unknown code (events, callbacks) while holding a lock.
- Take locks in one fixed order to avoid deadlocks.
- Don’t block on async code (
.Result,.Wait()); in ASP.NET Core it starves the thread pool. - You can’t
awaitinside alock; useSemaphoreSlim.
Readers and writers
var rw = new ReaderWriterLockSlim();
var prices = new Dictionary<string, int> { ["tea"] = 3 };
rw.EnterReadLock(); try { Console.WriteLine(prices["tea"]); } finally { rw.ExitReadLock(); }
rw.EnterWriteLock(); try { prices["tea"] = 4; } finally { rw.ExitWriteLock(); }
Console.WriteLine(prices["tea"]);
It prints 3, then 4. Many threads can hold the read lock together; the write lock is exclusive. For short sections a plain lock is often just as fast.
Read more: https://learn.microsoft.com/en-us/dotnet/standard/threading/managed-threading-best-practices
#tip · TIP-082
Write a comment