Process Synchronization & the Critical-Section Problem

binary vs counting semaphores

Semaphores come in two everyday shapes, and the only difference is the range of the counter. A counting semaphore lets its count rise as high as you like; it tracks how many of several interchangeable resources are available. A binary semaphore restricts the count to just 0 or 1; it tracks a single yes/no condition — free or taken, signaled or not. Think of a parking garage sign: a counting semaphore is the 'SPACES: 37' display counting many spots, while a binary semaphore is a single 'OCCUPIED / VACANT' sign on one restroom.

A counting semaphore initialized to N admits up to N threads at once and is exactly the tool for a pool of N identical resources: N buffer slots, N network connections, N worker permits. Each wait takes one permit (blocking when none remain), each signal returns one. A binary semaphore initialized to 1 admits exactly one thread at a time, which is precisely mutual exclusion — so a binary semaphore can be used as a lock. In fact a counting semaphore can be simulated using binary semaphores and an integer, and vice versa, so the two are equivalent in power; the distinction is about what you are counting, not what is possible.

An honest caution about the binary-semaphore-as-lock idea: a binary semaphore is similar to a mutex but not identical, and the difference matters. A mutex usually has the notion of an owner — only the thread that locked it should unlock it — whereas a classic binary semaphore has no owner: any thread may signal it, including one that never waited. That flexibility is useful for signaling events between different threads, but it removes the safety check that catches 'unlocking a lock you do not hold', and it means a binary semaphore cannot offer features that depend on ownership, such as priority inheritance to combat priority inversion. So: use a counting semaphore to ration N resources, a mutex for ownership-based mutual exclusion, and a binary semaphore mainly when you genuinely need cross-thread signaling rather than a lock.

A bounded buffer with 10 slots uses a counting semaphore empty = 10 (producers wait on it) and full = 0 (consumers wait on it). A separate binary semaphore mutex = 1 guards the buffer's internal pointers. The counting one rations slots; the binary one enforces one-at-a-time access.

Counting (0..N) rations many resources; binary (0/1) is a single yes/no — usable as a lock.

A binary semaphore is not exactly a mutex: a classic semaphore has no owner (any thread may signal it) and no priority inheritance, so prefer a real mutex for ownership-based locking.

Also called
binary semaphorecounting semaphore二元號誌計數號誌