C++ Standard Library<condition_variable>

In multithreaded programming, synchronization between threads is a very important issue.

The C++11 standard introduced<condition_variable>The header file provides a mechanism that allows threads to suspend when certain conditions are not met, until other threads notify them that the conditions have been satisfied.

condition_variableIt is an advanced tool for synchronization between threads, which is safer and more convenient than using low-level synchronization primitives (such as mutexes and condition variables).

condition_variableIt is a class template used to implement synchronization between threads in a multithreaded environment. It allows one or more threads to wait for a certain condition to become true, while other threads can wake up these waiting threads.

Syntax

The following arecondition_variablebasic syntax:

#include <condition_variable>

void notify_one() {
    // 唤醒一个等待的线程
}

void notify_all() {
    // 唤醒所有等待的线程
}

template <class Mutex>
class condition_variable {
public:
    condition_variable();
    ~condition_variable();

    void wait(unique_lock<mutex>& lock);
    void wait_for(unique_lock<mutex>& lock, chrono::duration<Rep, Period> const& rel_time);
    void wait_until(unique_lock<mutex>& lock, chrono::time_point<Clock, Duration> const& abs_time);

    void notify_one() noexcept;
    void notify_all() noexcept;
};

Example

Below is an example usingcondition_variableA simple example of it, demonstrating the basic implementation of the producer-consumer problem.

Example

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

std::mutex mtx;
std::condition_variable cv;
std::queue<int> product;

void producer(int id) {
    for (int i = 0; i < 5; ++i) {
        std::unique_lock<std::mutex> lck(mtx);
        product.push(id * 100 + i);
        std::cout << "Producer " << id << " produced " << product.back() << std::endl;
        cv.notify_one();
        lck.unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lck(mtx);
        cv.wait(lck, []{ return !product.empty(); });
        if (!product.empty()) {
            int prod = product.front();
            product.pop();
            std::cout << "Consumer " << id << " consumed " << prod << std::endl;
        }
        lck.unlock();
    }
}

int main() {
    std::thread producers[2];
    std::thread consumers[2];

    for (int i = 0; i < 2; ++i) {
        producers[i] = std::thread(producer, i + 1);
    }

    for (int i = 0; i < 2; ++i) {
        consumers[i] = std::thread(consumer, i + 1);
    }

    for (int i = 0; i < 2; ++i) {
        producers[i].join();
        consumers[i].join();
    }

    return 0;
}

The output of this program may vary because it depends on thread scheduling. However, you will see the producer produce items, and then the consumer consume them. The output will be similar to:

Producer 1 produced 100
Producer 2 produced 200
Consumer 1 consumed 100
Producer 1 produced 101
Consumer 2 consumed 200
Producer 2 produced 201
Consumer 1 consumed 101
...

Notes

  • Usagecondition_variableWhen doing so, you must ensure that the mutex is acquired before waiting, and released after being woken up.
  • wait、wait_forandwait_untilThe functions release the mutex, and then reacquire it during the wait.
  • notify_onewakes up one waiting thread, whilenotify_allwakes up all waiting threads.
other extensions