Binary Semaphore vs Counting Semaphore
Binary vs counting semaphore compared: value range, shared wait/signal mechanics, and when to use each, with examples.
Expected Interview Answer
A binary semaphore holds only two states, 0 and 1, and is used to guard a single resource much like a mutex, while a counting semaphore holds an integer that can range from 0 up to a configured maximum N, letting up to N threads hold a unit of a resource pool concurrently.
Both are built on the identical wait/signal mechanism β wait atomically decrements the value and blocks the caller if it would go negative, signal atomically increments it and wakes a waiter if the queue is non-empty β the only real difference is the range the counter is allowed to take. A binary semaphore is initialized to 1, so after one wait call the value is 0 and any further wait call blocks, exactly modeling exclusive access to one resource; a counting semaphore is initialized to N, so up to N wait calls can succeed before the value hits 0 and further callers block, modeling a pool of N interchangeable resources like database connections or buffer slots. A subtle but important distinction from a true mutex is that a binary semaphore still has no ownership: any thread can call signal on it, even one that never called wait, which makes it usable for cross-thread signaling patterns (like signaling that an event occurred) that a strict mutex cannot support. Choosing between them is really about the resource shape β exactly one instance versus a fixed-size pool of N β rather than about performance, since the underlying implementation cost is the same.
- Binary semaphore models exclusive access to a single resource
- Counting semaphore models a fixed pool of N interchangeable resources
- Both share identical wait/signal machinery, differing only in value range
- Binary semaphore (unlike a mutex) supports cross-thread signaling since it has no ownership
AI Mentor Explanation
A binary semaphore is like a single umpireβs spare stopwatch that only one player at a time can be handed β after it is checked out, the count of spares drops to zero and nobody else can take it until it is returned. A counting semaphore is like a rack of five practice balls the squad shares: up to five players can each take one at once, and only the sixth player has to wait, since the rack tracks a count rather than a single yes/no flag. Both use the same checkout-and-return system underneath; only the starting number of items differs.
Step-by-Step Explanation
Step 1
Choose initial value
Binary semaphore starts at 1 (one resource); counting semaphore starts at N (a pool of N resources).
Step 2
wait (P) decrements
Each successful wait atomically lowers the counter by one; a binary semaphore only allows one success before hitting zero, a counting semaphore allows up to N.
Step 3
Callers block past the limit
Once the value would go negative, further wait calls enqueue the caller and block, regardless of whether it is binary or counting.
Step 4
signal (V) increments and wakes
Each signal raises the counter and wakes a waiter if any are queued, in exactly the same way for both variants.
What Interviewer Expects
- Correctly stating binary = 0/1 range, counting = 0..N range
- Recognizing both share identical wait/signal implementation underneath
- Giving a concrete counting semaphore use case (resource pool)
- Noting a binary semaphore still lacks ownership, unlike a true mutex
Common Mistakes
- Claiming a binary semaphore and a mutex are exactly the same thing
- Thinking counting semaphores need a fundamentally different implementation
- Forgetting a counting semaphore can also fully block once it hits zero
- Not being able to name a concrete counting-semaphore use case
Best Answer (HR Friendly)
βA binary semaphore is essentially a single yes/no flag for one resource, similar in spirit to a lock, while a counting semaphore is the same mechanism generalized to a number, letting a fixed number of threads share a pool of resources at once. Under the hood they work identically β atomically decrement to take, atomically increment to give back β the only difference is whether that number can only be 0 or 1, or can range up to N.β
Code Example
#include <semaphore.h>
sem_t binary_sem; /* models one resource: 0 or 1 */
sem_t counting_sem; /* models a pool of N resources */
void init_semaphores(void) {
sem_init(&binary_sem, 0, 1); /* starts at 1: one holder at a time */
sem_init(&counting_sem, 0, 4); /* starts at 4: up to 4 concurrent holders */
}
void use_single_resource(void) {
sem_wait(&binary_sem); /* only one thread proceeds past this line */
/* ... exclusive access ... */
sem_post(&binary_sem);
}
void use_pooled_resource(void) {
sem_wait(&counting_sem); /* up to 4 threads proceed concurrently */
/* ... access one of 4 interchangeable resources ... */
sem_post(&counting_sem);
}Follow-up Questions
- Why is a binary semaphore not exactly the same as a mutex?
- Give a concrete real-world use case for a counting semaphore.
- What happens if a counting semaphoreβs value drops below zero β is that allowed?
- How would you implement a counting semaphore using a mutex and a condition variable?
MCQ Practice
1. What value range does a binary semaphore take?
A binary semaphore is constrained to exactly two states, 0 and 1, modeling a single available resource.
2. What is a counting semaphore best used for?
A counting semaphore generalizes the binary case to a pool of N interchangeable resources, permitting up to N concurrent holders.
3. How does a binary semaphore differ from a true mutex?
Unlike a mutex, which enforces that only the locking thread can unlock, a binary semaphore carries no ownership, so any thread may call signal on it.
Flash Cards
Binary semaphore value range? β 0 or 1 only, modeling exclusive access to a single resource.
Counting semaphore value range? β 0 up to a configured maximum N, modeling a pool of N resources.
Do binary and counting semaphores share implementation? β Yes β identical wait/signal machinery, differing only in the initial value and range.
How does a binary semaphore differ from a mutex? β It has no ownership β any thread can signal it, even one that never called wait.