Compare
Side-by-side on the decisions that recur: process vs thread, threads vs async, mutex vs semaphore, blocking vs non-blocking I/O, container vs VM — with when to choose each.
Process vs ThreadThreads vs Async / event loopConcurrency vs ParallelismMutex vs SemaphoreBlocking I/O vs Non-blocking / async I/Oselect / poll vs epoll / kqueueContainer vs Virtual machineStack vs HeapPipe vs Shared memoryOS page cache vs Application cache
| Mutex | Semaphore | |
|---|---|---|
| Concept | A lock with an owner: one holder at a time | A counter: up to N holders, wait decrements, post increments |
| Who may release | Only the thread that locked it (undefined or an error otherwise) | Any thread — a producer can post what a consumer waits on |
| Use for | Protecting a critical section around shared state | Limiting concurrent access to N resources; signalling between threads |
| Binary semaphore | Not the same: a mutex has ownership and priority-inheritance semantics | A semaphore with N = 1 — works as a signal, not as a lock |
| Implementation | pthread_mutex_t, std::mutex; Linux futex fast path, Windows CRITICAL_SECTION / SRW lock | POSIX sem_t, std::counting_semaphore (C++20), Windows CreateSemaphore, asyncio.Semaphore |
| Classic bug | Locking in inconsistent order across two mutexes → deadlock | Forgetting a post on an error path → the pool silently shrinks to zero |
| Choose this when | You need mutual exclusion around a read-modify-write of shared data, and the same thread will unlock it. | You need "at most N at once" (connection pool, rate cap) or a producer-consumer hand-off where different threads signal and wait. |