Synchronization

a semaphore

/ SEM-uh-for /

Picture a small car park with a sign out front showing the number of free spaces. A car arriving decrements the sign and drives in; if the sign reads zero, the driver waits at the gate until a space opens. A car leaving increments the sign and lets a waiting driver in. A semaphore is exactly that counting sign for threads: an integer that never goes below zero, with two atomic operations to decrement and increment it. It was invented by Edsger Dijkstra in the 1960s.

The two operations are historically called P (from a Dutch word, 'try to decrease') and V ('increase'), more readably named wait and post (or down and up, or acquire and release). wait/P decrements the counter; if the counter is already zero, the calling thread blocks until it can decrement. post/V increments the counter, waking one blocked waiter if any. A counting semaphore initialised to N lets up to N threads through at once — perfect for limiting access to N identical resources (N database connections, N buffer slots). A binary semaphore is one whose count is only ever 0 or 1, so it behaves like a lock — but unlike a mutex it has no notion of an owner: any thread may post it, including one that never waited. That ownerless quality makes a semaphore the natural tool for signalling between threads, where one thread posts and a different thread waits.

Semaphores are more general than mutexes and condition variables, and you can build either from them — but that generality is also the trap. Because a semaphore carries no owner and no associated lock invariant, it is easy to post one too many times, or to forget a wait, and produce subtle bugs. The modern advice is to use a mutex when you mean mutual exclusion, a condition variable when you mean 'wait for a state', and reserve semaphores for counting a resource or for simple signalling, where their count genuinely models something real.

To cap concurrent downloads at 3: sem_t s; sem_init(&s, 0, 3); each worker does sem_wait(&s); download(); sem_post(&s);. At most three workers hold a slot at once; the fourth blocks in sem_wait until one posts.

A counting semaphore of 3 admits three threads at once; wait decrements, post increments.

A semaphore has no owner, so unlike a mutex any thread can post it and there is no built-in detection of 'unlocking a lock you do not hold'. Using a binary semaphore as a mutex loses ownership checks and priority-inheritance, so prefer a real mutex for mutual exclusion.

Also called
counting semaphorebinary semaphoreP/V信號量