Table of Contents

Semaphore

A semaphore is a counter, manipulated only through two atomic operations traditionally called wait (or P, acquire) and signal (or V, release). wait decrements the counter and blocks the calling thread if the result would go negative; signal increments the counter and wakes a waiting thread if one is blocked. Edsger Dijkstra introduced the concept in 1965 as a general tool for both mutual exclusion and coordination.

#include <semaphore.h>
 
sem_t sem;
sem_init(&sem, 0, 3);   // initial count of 3: up to 3 concurrent holders
 
void use_resource(void) {
    sem_wait(&sem);     // acquire: blocks if count is already 0
    /* ... use one of 3 available resource slots ... */
    sem_post(&sem);     // release: increments count, wakes a waiter if any
}

Counting vs binary

A counting semaphore initialized to N allows up to N threads to pass wait before any of them blocks, making it a natural fit for limiting concurrent access to a pool of N identical resources (database connections, worker slots). A binary semaphore is the special case initialized to 1, which looks like a mutex on the surface but is not the same thing.

Why it isn't a mutex

A semaphore has no concept of ownership: any thread can call signal, not just the thread that called wait. This makes a binary semaphore usable for a pattern a mutex cannot express directly, thread A waits for a signal that thread B sends, since a mutex assumes whoever locked it will be the one to unlock it. The cost of that flexibility is that a semaphore can't protect you from a bug where the wrong thread releases it; a mutex's ownership check exists specifically to catch that class of mistake.