Fundamentals 9 min read

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.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Mastering pthread Condition Variables: Efficient Thread Wait‑Notify in Linux

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_wait

performs 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

condition variablepthreadthread synchronizationc-programming
Liangxu Linux
Written by

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.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.