第 18.19.4 節

condition_variable

0瀏覽次數0訪問次數--跳出率--平均停留

本節解決什麼問題

std::condition_variable 用於"一個線程等待某個條件,另一個線程改變條件後發通知"。

典型例子是生產者消費者:生產者把數據放進隊列,消費者沒有數據時等待,有數據後被喚醒。

條件變量通常和 std::unique_lock<std::mutex> 一起使用,因為 wait() 需要在等待時臨時釋放鎖,被喚醒後再重新加鎖。

示例代碼

示例 1:生產者消費者

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

std::queue<int> data_queue;
// 加锁用来保护共享数据,避免多个线程同时修改造成数据竞争。
std::mutex queue_mutex;
std::condition_variable queue_cv;
bool finished = false;

void producer()
{
    for (int value = 1; value <= 3; ++value)
    {
        {
            std::lock_guard<std::mutex> lock(queue_mutex);
            data_queue.push(value);
            std::cout << "produced " << value << "\n";
        }

        queue_cv.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }

    {
        std::lock_guard<std::mutex> lock(queue_mutex);
        finished = true;
    }
    queue_cv.notify_one();
}

void consumer()
{
    while (true)
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        queue_cv.wait(lock, [] {
            return !data_queue.empty() || finished;
        });

        while (!data_queue.empty())
        {
            int value = data_queue.front();
            data_queue.pop();

            lock.unlock();
            std::cout << "consumed " << value << "\n";
            lock.lock();
        }

        if (finished)
        {
            break;
        }
    }
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // 创建子线程,让这部分代码和 main 线程并发运行。
    std::thread p(producer);
    std::thread c(consumer);

    // join 会等待子线程结束,避免 main 提前退出。
    p.join();
    c.join();

    std::cout << "all done\n";

    return 0;
}

一種可能的運行結果

produced 1
consumed 1
produced 2
consumed 2
produced 3
consumed 3
all done

queue_cv.wait(lock, 条件) 很重要。線程被喚醒後會重新檢查條件,避免虛假喚醒導致邏輯錯誤。

示例 2:等待一次通知

如果只是一個線程等待另一個線程準備好數據,也可以用條件變量。這個例子比生產者消費者更短,適合先理解 waitnotify_one 的配合。

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

// 加锁用来保护共享数据,避免多个线程同时修改造成数据竞争。
std::mutex data_mutex;
std::condition_variable data_cv;
std::string message;
bool ready = false;

void prepare()
{
    {
        std::lock_guard<std::mutex> lock(data_mutex);
        message = "hello from worker";
        ready = true;
    }

    data_cv.notify_one();
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // 创建子线程,让这部分代码和 main 线程并发运行。
    std::thread worker(prepare);

    std::unique_lock<std::mutex> lock(data_mutex);
    data_cv.wait(lock, [] {
        return ready;
    });

    std::cout << message << "\n";

    lock.unlock();
    // join 会等待子线程结束,避免 main 提前退出。
    worker.join();

    return 0;
}

運行結果

hello from worker

常見錯誤

  1. wait() 不寫條件謂詞,只靠一次喚醒,容易被虛假喚醒影響。
  2. 修改共享條件後忘記 notify_one()notify_all()
  3. 持鎖期間做耗時處理,導致其他線程不能及時推進。
  4. 沒有把條件變量、互斥鎖、共享條件放在同一套邏輯裏維護。

小結

  • condition_variable 用於線程間等待和通知。
  • wait() 通常要配合 unique_lock
  • 推薦寫 cv.wait(lock, [] { return 条件; });
  • 修改條件後調用 notify_one()notify_all()
  • 條件變量適合生產者消費者、任務隊列、等待初始化完成等場景。
音乐页