
Click the blue text above to follow us
In the Linux environment, condition variables are a mechanism for thread synchronization that allows threads to enter a waiting state when a certain condition is not met, and to be notified by other threads when shared resources or conditions are modified.
Condition variables are typically used in conjunction with mutexes to ensure safe access to shared resources.
By using condition variables, threads can avoid busy waiting, thus improving efficiency.
Condition variables allow threads to synchronize in the following ways:
-
Waiting for a condition to be met: When a thread is waiting for a certain condition, it can enter a blocked state and release the held mutex, allowing other threads to operate on the shared resources.
-
Notifying that a condition has been met: When other threads change the state of shared resources and meet the waiting thread’s condition, they can send a signal to notify the waiting threads, waking them up to continue execution.
Condition variables are typically used with mutexes because concurrent access to shared resources must be protected during checks or modifications to prevent race conditions.
Mutexes are responsible for protecting shared resources, while condition variables are responsible for passing state information between threads.
The specific steps are as follows:
-
Threads access shared resources through mutexes.
-
When the condition is not met, threads enter a wait state through the condition variable and release the mutex, allowing other threads to continue operating on the resources.
-
Other threads modify the shared resources and send a signal indicating that the condition is met, notifying the condition variable to wake the waiting threads.
-
The awakened thread re-acquires the mutex and checks if the condition is met; if it is, it continues execution; otherwise, it continues to wait.
Steps to use condition variables:
-
Initialize the condition variable and mutex.
-
Use the mutex in threads to protect shared resources.
-
If the condition is not met, call pthread_cond_wait() to block the thread while releasing the mutex, waiting for other threads to notify that the condition is met.
-
After modifying the shared resources, other threads call pthread_cond_signal() or pthread_cond_broadcast() to notify the waiting threads.
-
The awakened thread re-acquires the mutex and continues to check the condition.
1
Initialization and Destruction of Condition Variables
Condition variables use the pthread_cond_t data type to represent them, similar to mutexes, and must be initialized before use.
They can be initialized in two ways:
1. Using the macro PTHREAD_COND_INITIALIZER, similar to the static initialization method of mutexes:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2. Using the pthread_cond_init() function for dynamic initialization of the condition variable:
pthread_cond_init(&cond, NULL); // attr is NULL for default attributes
Parameters:
-
cond: Pointer to the condition variable object to be initialized, of type pthread_cond_t*.
-
attr: Attributes of the condition variable, can be NULL to indicate default attributes.
Return Value:
-
Returns 0 on success;
-
Returns a non-zero error code on failure, such as EINVAL (invalid attribute).
When the condition variable is no longer in use, it should be destroyed using pthread_cond_destroy() to free system resources, ensuring that no threads are waiting on that condition variable before destruction.
pthread_cond_destroy(&cond);
Parameters:
-
cond: Pointer to the condition variable object to be destroyed.
Return Value:
-
Returns 0 on success;
-
Returns a non-zero error code on failure, such as EBUSY (threads are waiting on that condition variable).
Notes:
-
Only initialized condition variables can be destroyed.
-
When destroying a condition variable, no threads should be waiting on it, or it will lead to undefined behavior.
2
Sending Signals and Waiting on Condition Variables
The core functionality of condition variables is to send signals and wait for conditions.
In multithreaded programs, threads wait for conditions using pthread_cond_wait(), while other threads send signals using pthread_cond_signal() or pthread_cond_broadcast().
pthread_cond_wait(): Causes a thread to wait for the condition to be met while releasing the mutex during the wait period. This function blocks the calling thread until another thread calls pthread_cond_signal() or pthread_cond_broadcast() to notify the condition variable.
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
Parameters:
-
cond: Pointer to the condition variable, pointing to a pthread_cond_t type condition variable.
-
mutex: Pointer to the mutex, pointing to a pthread_mutex_t type mutex. When the thread enters the wait state, pthread_cond_wait automatically releases the mutex and re-locks it when the thread is awakened.
Return Value:
-
Returns 0 on success;
-
Returns a non-zero error code on failure.
Notes:
-
The mutex must be locked before calling pthread_cond_wait() to avoid race conditions between condition checks and waiting.
-
When the thread is awakened, it will re-lock the passed mutex.
pthread_cond_signal(): Used to notify at least one thread waiting on the condition variable, waking it from the blocked state of pthread_cond_wait(). If no threads are waiting on the condition variable, the signal is discarded.
int pthread_cond_signal(pthread_cond_t *cond);
Parameters:
-
cond: Pointer to the condition variable, pointing to a pthread_cond_t type condition variable.
Return Value:
-
Returns 0 on success;
-
Returns a non-zero error code on failure.
Notes:
-
This function can only wake one waiting thread.
-
If no threads are waiting, the signal will not accumulate, so calling this function when no threads are waiting will have no effect.
pthread_cond_broadcast(): Used to wake all threads waiting on that condition variable. Suitable for scenarios where multiple threads need to be awakened.
int pthread_cond_broadcast(pthread_cond_t *cond);
Parameters:
-
cond: Pointer to the condition variable, pointing to a pthread_cond_t type condition variable.
Return Value:
-
Returns 0 on success;
-
Returns a non-zero error code on failure.
Assuming there are multiple consumer threads waiting for products, if pthread_cond_signal() is used, only one thread will be awakened.
If we want all consumers to be awakened, we use pthread_cond_broadcast().
However, generally speaking, pthread_cond_signal() is more efficient unless all threads need to be awakened.
pthread_cond_signal(&cond); // Wake one thread
pthread_cond_broadcast(&cond); // Wake all waiting threads
3
Condition Checking with Condition Variables
When using condition variables, it usually involves a shared variable (like the buffer in the example above).
When checking the condition, a while loop must be used instead of an if statement. This is because:
-
Spurious wake-ups: A thread may be accidentally awakened, but the condition is still not met. Using a while loop ensures the condition is checked again instead of immediately continuing execution.
-
Race conditions: Multiple threads may be awakened simultaneously, but only one thread will successfully acquire the lock and modify the condition state. Other threads must wait again.
This design ensures that threads do not continue execution when the condition is not met, thus avoiding issues caused by race conditions.
pthread_mutex_lock(&mutex);while (buffer == 0) { pthread_cond_wait(&cond, &mutex);}
The following example demonstrates the producer-consumer model, where producer and consumer threads synchronize using condition variables:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int buffer = 0; // Buffer, 0 indicates empty, 1 indicates product available
void *producer(void *arg) { pthread_mutex_lock(&mutex); // Lock while (buffer == 1) { pthread_cond_wait(&cond, &mutex); // Buffer full, wait } buffer = 1; // Produce product printf("Produced an item\n"); pthread_cond_signal(&cond); // Notify consumer pthread_mutex_unlock(&mutex); // Unlock return NULL;}
void *consumer(void *arg) { pthread_mutex_lock(&mutex); // Lock while (buffer == 0) { pthread_cond_wait(&cond, &mutex); // Buffer empty, wait } buffer = 0; // Consume product printf("Consumed an item\n"); pthread_cond_signal(&cond); // Notify producer pthread_mutex_unlock(&mutex); // Unlock return NULL;}
int main() { pthread_t prod, cons;
// Create producer and consumer threads pthread_create(&prod, NULL, producer, NULL); pthread_create(&cons, NULL, consumer, NULL);
// Wait for threads to finish pthread_join(prod, NULL); pthread_join(cons, NULL);
// Destroy mutex and condition variable pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond);
return 0;
Condition variables in Linux are a powerful tool for thread synchronization, allowing threads to wait for specific conditions to be met before executing operations, thereby avoiding ineffective busy waiting.
By collaborating with mutexes, condition variables can effectively coordinate access to shared resources between threads, ensuring safety and efficiency in concurrent environments.
-
Condition variables used with mutexes: Condition variables are used to wait for and notify condition changes, while mutexes protect access to shared resources.
-
Condition variables do not save state: If no threads are waiting on the condition variable, the signal is lost.
-
pthread_cond_wait() should be placed in a loop: Because multiple threads may compete for resources, conditions should be re-checked after returning from pthread_cond_wait().
-
Select the appropriate notification method: pthread_cond_signal() wakes one waiting thread, while pthread_cond_broadcast() wakes all waiting threads.

