What synchronization primitives does .NET have (lock, Monitor, SemaphoreSlim, etc.)?
Quick Answer
lock (syntactic sugar over Monitor) — mutual exclusion for a short critical section, one thread at a time. SemaphoreSlim — allow N concurrent, and it has an async WaitAsync (you can't await inside a lock). ReaderWriterLockSlim — many readers or one writer. Interlocked — lock-free atomic increment/exchange. Mutex — cross-process.
lock is the everyday one — it's Monitor underneath, and it gives one thread at a time exclusive access to a short critical section. If I need to allow, say, three concurrent operations, that's SemaphoreSlim, and crucially it has an async WaitAsync, because you can't await inside a lock — that's a classic deadlock. ReaderWriterLockSlim is for many-readers-one-writer. Interlocked is lock-free atomic operations for things like counters. And I lock on a private object, never on this or a string, and I keep the locked region as small as possible — or better, use a concurrent collection so I don't lock at all.
- Never
awaitinsidelock— the lock isn't released across the await and it can deadlock. UseSemaphoreSlim(1,1)withawait WaitAsync()/Release(). - Prefer concurrent collections (
ConcurrentDictionary,ConcurrentQueue) and immutable data over hand-rolled locking. Interlocked.Incrementfor counters — faster than a lock.- Keep critical sections tiny; lock on a private
readonly object, never onthis, a type, or a string.