Synchronization PatternsThread Signaling & CoordinationMedium⏱️ ~2 min

Condition Variables vs Semaphores for Signaling

Key Insight
Semaphores and condition variables both enable signaling, but they have different memory semantics. Semaphores remember signals. Condition variables do not.

Semaphores Remember

When you signal a semaphore, its count increments. If no one is waiting, the signal is stored. A later wait() will succeed immediately. The semaphore has memory - it counts how many signals have been sent.

Condition Variables Forget

When you signal a condition variable and no one is waiting, the signal is lost. Gone. If a thread calls wait() later, it blocks even though a signal was sent. Condition variables are stateless.

Signal Before Wait: What Happens?
Semaphoresignal() → count = 1wait() → count = 0, proceed!Condition Variablesignal() → no one waiting, lost!wait() → blocks forever

When to Use Which

Semaphores: When signals must not be lost. When counting matters. When the signal and wait might happen in any order. Resource counting, permits, simple signaling.

Condition variables: When you need to wait for a condition to become true, not just receive a signal. When combined with a mutex to check and modify shared state atomically. Producer-consumer with complex conditions.

The Condition Variable Pattern

Always use condition variables inside a loop: while (!condition) { cv.wait(); }. The loop handles spurious wakeups and lost signals. Check the actual condition, not just whether you were signaled.

Common Mistake: Using a condition variable like a semaphore. If you signal when no one is waiting, that signal is gone. Always pair condition variables with a predicate check in a while loop.
💡 Key Takeaways
Semaphores have memory. A signal increments a counter. Later waits can consume stored signals.
Condition variables are stateless. Signal when no one waits? Signal lost. This is by design.
Use semaphores for counting and permits. Use condition variables for waiting on complex conditions.
Condition variables must be used in a while loop checking the actual condition. This handles spurious wakeups.
Wrong tool choice causes bugs. Lost signals with condition variables, or complex logic with semaphores.
📌 Examples
1Resource permits: Semaphore is ideal. 5 permits available, threads acquire and release. Count is tracked automatically.
2Queue not empty: Condition variable with while(queue.empty()) { cv.wait(); }. Check the real condition, not just the signal.
3Lost signal bug: Thread A signals before Thread B waits. With semaphore, B proceeds. With condition variable, B blocks forever.
← Back to Thread Signaling & Coordination Overview