Mastering pthread Condition Variables: Efficient Thread Wait‑Notify in Linux
This article explains the core principles, API usage, and best‑practice steps for pthread condition variables in embedded Linux, showing why they must be paired with mutexes, how to avoid lost wake‑ups, when to use signal versus broadcast, and includes a complete C example with practical tips.
Core Principle
What a Condition Variable Is
A condition variable, provided by the pthread library, is a thread‑notification mechanism that signals when a specific condition becomes true. It does not store data and does not provide mutual exclusion; it only handles thread sleep and wake‑up and must be used together with a mutex.
Why It Must Be Paired with a Mutex
In a producer‑consumer scenario, checking the condition and entering the wait state must be atomic. Without a mutex, a lost wake‑up can occur, causing threads to miss the signal or act on stale conditions.
Why pthread_cond_wait Requires a Mutex
pthread_cond_waitperforms an atomic "unlock‑then‑sleep" operation: it releases the mutex, puts the thread to sleep, and upon waking re‑acquires the mutex before returning, guaranteeing thread‑safe continuation.
pthread_cond_signal vs pthread_cond_broadcast
pthread_cond_signal : wakes at least one waiting thread; low overhead; suitable for one‑to‑one notifications.
pthread_cond_broadcast : wakes all waiting threads; higher overhead; used for one‑to‑many notifications.
Why Use a while Loop Instead of if
Using while(condition == false) protects against spurious wake‑ups and condition changes after a thread is awakened, ensuring the condition is truly satisfied before proceeding.
Typical Steps for Using Condition Variables
Waiter (Consumer) Steps
Lock the mutex with pthread_mutex_lock.
Loop checking the condition (e.g., while (!ready)).
If the condition is not met, call pthread_cond_wait to sleep.
After being awakened, re‑check the condition and execute business logic.
Unlock the mutex with pthread_mutex_unlock.
Notifier (Producer) Steps
Lock the mutex.
Modify the condition (e.g., set a flag or add data to a queue).
Send a notification with pthread_cond_signal or pthread_cond_broadcast.
Unlock the mutex.
Complete C Example
The program below creates a producer thread that signals when a counter reaches a multiple of five and a consumer thread that waits for that condition.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
static pthread_t s_thread1_id;
static pthread_t s_thread2_id;
static volatile unsigned char s_thread1_running = 0;
static volatile unsigned char s_thread2_running = 0;
static pthread_mutex_t s_mutex;
static pthread_cond_t s_cond;
static int s_cnt = 0;
static int s_processed = 0; // avoid duplicate processing
void *thread1_fun(void *arg) {
s_thread1_running = 1;
while (s_thread1_running) {
pthread_mutex_lock(&s_mutex);
s_cnt++;
if (s_cnt % 5 == 0) {
s_processed = 0;
pthread_cond_signal(&s_cond);
printf("[%s] s_cnt = %d, sent signal
", __FUNCTION__, s_cnt);
} else {
printf("[%s] s_cnt = %d
", __FUNCTION__, s_cnt);
}
pthread_mutex_unlock(&s_mutex);
usleep(100 * 1000);
}
pthread_exit(NULL);
}
void *thread2_fun(void *arg) {
s_thread2_running = 1;
while (s_thread2_running) {
pthread_mutex_lock(&s_mutex);
while (s_cnt % 5 != 0 || s_processed) {
pthread_cond_wait(&s_cond, &s_mutex);
}
printf("[%s] s_cnt = %d, condition met, processing
", __FUNCTION__, s_cnt);
s_processed = 1;
pthread_mutex_unlock(&s_mutex);
usleep(200 * 1000);
}
pthread_exit(NULL);
}
int main(void) {
int ret = 0;
if ((ret = pthread_mutex_init(&s_mutex, NULL)) != 0) {
printf("pthread_mutex_init error!
");
exit(EXIT_FAILURE);
}
if ((ret = pthread_cond_init(&s_cond, NULL)) != 0) {
printf("pthread_cond_init error!
");
exit(EXIT_FAILURE);
}
if ((ret = pthread_create(&s_thread1_id, NULL, thread1_fun, NULL)) != 0) {
printf("thread1_create error!
");
exit(EXIT_FAILURE);
}
if ((ret = pthread_create(&s_thread2_id, NULL, thread2_fun, NULL)) != 0) {
printf("thread2_create error!
");
exit(EXIT_FAILURE);
}
sleep(10);
s_thread1_running = 0;
s_thread2_running = 0;
pthread_join(s_thread1_id, NULL);
pthread_join(s_thread2_id, NULL);
pthread_mutex_destroy(&s_mutex);
pthread_cond_destroy(&s_cond);
printf("Program exited normally
");
return 0;
}Practical Usage Tips
Do not call pthread_cond_wait while holding multiple locks; re‑locking order after wake‑up can cause deadlocks.
Ensure the lifetimes of the condition variable and its mutex match; avoid destroying them while threads may still call wait or signal.
Beware of the "thundering herd" effect: use pthread_cond_signal for one‑to‑one cases and limit pthread_cond_broadcast when many threads are waiting.
Conclusion
pthread condition variables provide an efficient wait‑notify mechanism that eliminates busy‑waiting and sleep‑based polling, reducing CPU usage while maintaining responsiveness. Correct usage hinges on three points: pairing the condition variable with a mutex atomically, looping the condition check with while, and selecting the appropriate signal or broadcast operation for the scenario.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
