134 / 163 · C11 · 8 min
Condition Variables
Condition variables let a thread sleep efficiently until a shared condition becomes true, avoiding useless spinning. They must be used together with a mutex: wait atomically drops the lock and sleeps, signal wakes a waiter, and an explicit state variable prevents lost signals.
In this lesson
The Waiting Problem Locks Cannot Solve
A mutex only guarantees exclusive access to a critical section; it cannot make a thread rest while a condition is still false. Repeatedly polling a flag wastes processor cycles. A primitive is therefore needed that places the thread on a sleep queue until another thread changes the relevant state and wakes it.
Semantics of Wait and Signal
wait must be invoked while the associated mutex is already held. The call atomically releases that lock and enqueues the caller; after being awakened the thread re-acquires the same lock before returning. signal simply selects one thread from the queue and makes it runnable. Both operations revolve around an explicit state variable that records the actual condition.
Three Iron Rules for Safe Use
First, always hold the lock when calling wait or signal. Second, re-test the condition with a while loop rather than an if, to survive spurious wake-ups. Third, keep a separate state variable; otherwise an early signal is discarded and the waiter may sleep forever.
Pitfalls
- Calling wait without holding the mutex immediately yields undefined behavior
- Testing the condition with if instead of while fails in the presence of spurious wake-ups
- Without a state variable a signal sent by a child that finishes first is silently lost
Run an example
Minimum C11 · complete program · Download .c
#include <stdio.h>
#include <pthread.h>
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
static int finished = 0;
static void *child_thread(void *arg) { (void)arg;
(void)arg;
printf("child running\n");
pthread_mutex_lock(&mtx);
finished = 1;
pthread_cond_signal(&cv);
pthread_mutex_unlock(&mtx);
return NULL;
}
int main(void) {
printf("parent starting\n");
pthread_t tid;
pthread_create(&tid, NULL, child_thread, NULL);
pthread_mutex_lock(&mtx);
while (finished == 0) {
pthread_cond_wait(&cv, &mtx);
}
pthread_mutex_unlock(&mtx);
printf("parent finishing\n");
pthread_join(tid, NULL);
return 0;
}
Compile locally
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror -pthread ostep-30-condition-variables.c -o example && ./exampleExpected result
parent starting
child running
parent finishing
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
What happens if the parent calls wait only after the child has already signaled, and the code contains no state variable such as done?
Show a reference answer
The signal is discarded, so the parent later enters wait and is never awakened; the program hangs.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.