What synchronization primitives does .NET have (lock, Monitor, SemaphoreSlim, etc.)?

.NET Framework & CLR Concurrency Advanced 3–5 Yrs5+ Yrs Conceptual 1 min read

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 await inside lock — the lock isn't released across the await and it can deadlock. Use SemaphoreSlim(1,1) with await WaitAsync() / Release().
  • Prefer concurrent collections (ConcurrentDictionary, ConcurrentQueue) and immutable data over hand-rolled locking.
  • Interlocked.Increment for counters — faster than a lock.
  • Keep critical sections tiny; lock on a private readonly object, never on this, a type, or a string.