A condition variable is a synchronization primitive that enables threads to wait until a particular condition becomes true. Unlike semaphores (which manage access to resources), condition variables manage waiting for a predicate — “wait until the queue is non-empty,” or “wait until the buffer has space.”
Why Not Just Use a Semaphore?
A semaphore can’t directly express “wait for this specific condition.” With a semaphore, you’d need to poll in a loop:
// Bad — busy-waiting
while (queue_is_empty()) {
// spin — wastes CPU
}
A condition variable gives you a way to sleep efficiently until someone signals that the condition might have changed.
Key Operations
| Operation | Effect |
|---|---|
wait(cv, mutex) | Atomically: release mutex, sleep on cv. When woken, re-acquire mutex. |
signal(cv) | Wake one thread waiting on cv (if any). |
broadcast(cv) | Wake all threads waiting on cv. |
The Correct Pattern
pthread_mutex_lock(&mtx);
while (!predicate) { // ALWAYS a while loop, not if
pthread_cond_wait(&cv, &mtx); // releases mutex, sleeps
} // when woken, re-acquires mutex
// ... use shared resource ...
pthread_mutex_unlock(&mtx);
Why while not if? Spurious wakeups can happen, and another thread might have consumed the condition between signal and wakeup.
Mesa vs Hoare Semantics
| Aspect | Hoare (theoretical) | Mesa (practical — used in pthreads, Java) |
|---|---|---|
| Signal behavior | Signaler immediately blocks, woken thread runs | Signaler continues, woken thread re-checks condition |
| Need to re-check? | No (guarantee) | Yes (while loop) |
| Implementation | Complex | Simple |
| Used in | Research systems | Linux, Java, C++, Windows |
Q: What is a condition variable?
A: A synchronization primitive that allows threads to wait for a specific condition to become true. Unlike a semaphore (which signals “resource available”), a condition variable signals “the state you’re waiting for might have changed — go check.”
Q: Why must you always use a while loop with condition variables?
A: Two reasons: (1) spurious wakeups — wait() can return without any signal, and (2) the condition might have been consumed by another thread between the signal and the wakeup. A while loop re-checks the predicate.
Q: What’s the difference between signal and broadcast?
A: signal wakes one waiting thread — appropriate when only one thread can proceed (e.g., “buffer has one item”). broadcast wakes all waiting threads — needed when the condition is complex and threads might need different predicates.
Q: What is the difference between Mesa and Hoare semantics?
A: In Hoare semantics, when signal() is called, the signaler blocks and the woken thread runs immediately — the condition is guaranteed. In Mesa semantics, the signaler continues, and the woken thread re-checks the condition in a while loop. All real systems use Mesa semantics.
Premium Content
Unlock Condition Variables and all premium lessons with a subscription.
From ₹199.99/year — See plans