Introduction
Semaphores are synchronization tools used when a simple lock is not enough. A lock mainly protects one critical section, but a semaphore can also count available permissions or signal that an event has happened.
A semaphore is an integer variable accessed through atomic operations. It can be understood as a counter with blocking behavior: if permission is not available, the thread can sleep instead of spinning.
Why Semaphores Are Needed
Locks are useful for mutual exclusion, where only one thread should enter a critical section. Some problems need a different idea: a fixed number of threads may be allowed to proceed, or one thread may need to wait for another thread’s event.
Semaphores solve two common synchronization needs:
Limited permits: Only a fixed number of threads can use a resource at the same time.
Event signaling: One thread waits until another thread announces that some work is complete.
For example, if a system has 10 database connections, a semaphore can start with value 10. Each worker takes one permit before using a connection and returns the permit afterward.
Wait and Signal Operations
A semaphore is mainly used through two atomic operations: wait(S) and signal(S).
wait(S): Takes one permit if available; otherwise blockssignal(S): Returns or announces one permit and may wake a waiting thread
wait(S) checks whether the semaphore count is greater than 0. If it is, the count is decreased and the thread continues. If the count is 0, the thread blocks until a permit becomes available.
signal(S) releases a permit. If a thread is waiting, one waiting thread may be woken. If no thread is waiting, the semaphore count can increase.
These operations are also known as P and V, or sometimes down and up.
Wait and Signal Operations
Binary and Counting Semaphores
Semaphores are usually discussed in two forms: binary semaphores and counting semaphores.
Type | Value Range | Main Use |
|---|---|---|
Binary semaphore |
| One permit, signaling, or simple mutual exclusion pattern |
Counting semaphore |
| Multiple permits for a limited resource pool |
A binary semaphore represents one permit. It can be used for event signaling or mutual exclusion-like behavior, but it does not have the same ownership meaning as a mutex.
A counting semaphore represents many permits. It is useful when there are multiple identical resources, such as printers, database connections, buffer slots, or worker slots.
Resource Counting Example
Suppose a system has 5 identical printers. The semaphore starts as: printers = 5
The flow is:
Thread needs printer: It calls
wait(printers).Permit available: The semaphore count decreases, and the thread uses one printer.
No permit available: The thread blocks because all printers are busy.
Thread finishes: It calls
signal(printers).Permit returns: A waiting thread may wake, or the count increases.
This prevents more than 5 threads from using the 5 printers at the same time.
Resource Counting & Event Ordering Example
Event Ordering Example
Semaphores can also force one action to happen after another. In this case, the semaphore is used for signaling, not resource counting.
Suppose Thread B must continue only after Thread A finishes some setup work. Use a semaphore named done and initialize it to 0.
The flow is:
Initial state:
done = 0, so the event has not happened yet.Thread B waits: Thread B calls
wait(done)and blocks.Thread A finishes: Thread A completes the required work.
Thread A signals: Thread A calls
signal(done).Thread B continues: Thread B wakes and proceeds.
This pattern means: do not continue until the required event has been signaled.
Semaphore vs Mutex
A binary semaphore can look similar to a mutex, but the meaning is different.
Aspect | Semaphore | Mutex |
|---|---|---|
Main idea | Permit counter or event signal | Mutual exclusion lock |
Ownership | Usually no strict owner | Locked and unlocked by the owner thread |
Common use | Counting resources or signaling events | Protecting a critical section |
Risk | Extra or missing signals corrupt the permit meaning | Wrong unlock violates ownership rules |
A mutex is best when the goal is ownership-based protection of shared state. A semaphore is best when the goal is permits, counting, or ordering between threads.
Common Semaphore Bugs
Semaphore correctness depends on matching the counter with the real resource or event.
Missing signal: A permit is taken but never returned, so other threads may block forever.
Extra signal: The count increases incorrectly and creates fake permits.
Wrong initial value: The semaphore no longer represents the actual number of resources or event state.
Using it as a mutex carelessly: Lack of ownership can make reasoning harder.
A good semaphore design always answers one question first: what exactly is this semaphore counting or signaling?
Summary
A semaphore is an integer synchronization variable used through atomic wait and signal operations. wait takes a permit or blocks, while signal returns a permit or wakes a waiting thread.
Binary semaphores represent one permit, and counting semaphores represent multiple permits. Semaphores are useful for resource pools, bounded buffers, database connections, printers, worker limits, and event ordering between threads.
Be the first to add a comment.