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.

MutexSemaphore
ConceptA lock with an owner: one holder at a timeA counter: up to N holders, wait decrements, post increments
Who may releaseOnly the thread that locked it (undefined or an error otherwise)Any thread — a producer can post what a consumer waits on
Use forProtecting a critical section around shared stateLimiting concurrent access to N resources; signalling between threads
Binary semaphoreNot the same: a mutex has ownership and priority-inheritance semanticsA semaphore with N = 1 — works as a signal, not as a lock
Implementationpthread_mutex_t, std::mutex; Linux futex fast path, Windows CRITICAL_SECTION / SRW lockPOSIX sem_t, std::counting_semaphore (C++20), Windows CreateSemaphore, asyncio.Semaphore
Classic bugLocking in inconsistent order across two mutexes → deadlockForgetting a post on an error path → the pool silently shrinks to zero
Choose this whenYou 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.