Fundamentals 8 min read

Mastering C++ Condition Variables: From Basics to Producer-Consumer Patterns

This article explains the concept, core features, basic usage, member functions, best practices, and real-world scenarios of C++11's std::condition_variable, including a complete producer‑consumer example and comparisons with atomic operations.

php Courses
php Courses
php Courses
Mastering C++ Condition Variables: From Basics to Producer-Consumer Patterns

In multithreaded programming, a condition variable is a powerful synchronization primitive that allows threads to be awakened only when a specific condition becomes true, avoiding busy‑waiting. C++11 introduced the standard std::condition_variable class.

1. Basic concept of condition variables

Condition variables are synchronization primitives used with a mutex to implement waiting and notification between threads. They solve the problem of a thread needing to wait for a condition without wasting CPU cycles; the thread sleeps and is awakened only when the condition may have changed.

Core characteristics:

Threads can block until a condition becomes true.

When the condition changes, waiting threads are notified.

Must be used together with a mutex to guarantee atomic condition checks.

2. Basic usage of std::condition_variable

Header inclusion

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

Basic structure

std::mutex mtx;
std::condition_variable cv;
bool ready = false; // condition variable

// waiting thread
void wait_thread() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return ready; });
    std::cout << "Thread awakened, condition satisfied" << std::endl;
}

// notifying thread
void notify_thread() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;
    }
    cv.notify_one(); // notify one waiting thread
}

3. Producer‑consumer example

The most typical use case for condition variables is the producer‑consumer pattern.

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
const int MAX_SIZE = 5;

void producer(int id) {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        // wait until queue is not full
        cv.wait(lock, []{ return data_queue.size() < MAX_SIZE; });
        data_queue.push(i);
        std::cout << "Producer " << id << " produced: " << i << std::endl;
        lock.unlock();
        cv.notify_all(); // notify consumers
    }
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        // wait until queue is not empty
        cv.wait(lock, []{ return !data_queue.empty(); });
        int data = data_queue.front();
        data_queue.pop();
        std::cout << "Consumer " << id << " consumed: " << data << std::endl;
        lock.unlock();
        cv.notify_all(); // notify producers
        if (data == 9) break; // termination condition
    }
}

int main() {
    std::thread p1(producer, 1);
    std::thread c1(consumer, 1);
    std::thread c2(consumer, 2);
    p1.join();
    c1.join();
    c2.join();
    return 0;
}

4. Member functions of condition_variable

wait()

// unconditional wait
void wait(std::unique_lock<std::mutex>& lock);

// wait with predicate (recommended)
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

notify functions

// notify one waiting thread
void notify_one() noexcept;

// notify all waiting threads
void notify_all() noexcept;

Timed wait functions

// wait for a duration
std::cv_status wait_for(std::unique_lock<std::mutex>& lock,
                        const std::chrono::duration<Rep,Period>& rel_time);

// wait for a duration with predicate
bool wait_for(std::unique_lock<std::mutex>& lock,
              const std::chrono::duration<Rep,Period>& rel_time,
              Predicate pred);

5. Best practices and pitfalls

Spurious wakeups

// wrong: may suffer from spurious wakeups
cv.wait(lock);

// correct: use a predicate
cv.wait(lock, []{ return ready; });

Mutex usage

Use std::unique_lock instead of std::lock_guard because wait() needs to unlock and relock the mutex.

Resource management

{
    std::lock_guard<std::mutex> lock(mtx);
    ready = true; // modify condition while holding the lock
}
cv.notify_one(); // send notification

6. Comparison with atomic operations

Condition variables are suited for complex synchronization conditions and do not consume CPU while waiting, whereas atomic operations are simple flag checks that may busy‑wait.

7. Real‑world scenarios

Task queues: scheduling work in a thread pool.

Resource pools: database or memory connection pools.

Event handling: event loops in GUI applications.

State synchronization: multiple threads waiting for a shared state.

Condition variables are an indispensable tool for efficient inter‑thread communication in C++.

Key takeaways

Always pair a condition variable with a mutex.

Use a predicate to guard against spurious wakeups.

Hold the lock while modifying the condition and release it after notifying.

Choose notify_one() or notify_all() according to 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.

CSynchronizationcondition variablemultithreadingProducer Consumer
php Courses
Written by

php Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

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.