云计算百科
云计算领域专业知识百科平台

C++11条件变量:线程同步核心技巧

好的,我们来详细探讨C++11标准库中的std::condition_variable(条件变量)。

🧩 一、条件变量是什么?

std::condition_variable 是C++11引入的线程同步原语,用于实现线程间的等待/通知机制。它允许一个或多个线程阻塞等待,直到被其他线程通知某个条件成立(或超时)。它通常与std::mutex和std::unique_lock配合使用。

🔑 二、核心方法

  • wait(unique_lock<mutex>& lock)

    • 当前线程阻塞,直到被notify_one()或notify_all()唤醒。
    • 在阻塞前,会自动释放传入的锁lock。
    • 被唤醒后,在返回前会重新获取锁lock。
    • 存在虚假唤醒的可能(即使未被通知也可能唤醒),因此通常需要在循环中检查条件。
  • wait(unique_lock<mutex>& lock, Predicate pred)

    • 带谓词(Predicate)的重载版本。
    • 等价于:

      while (!pred()) {
      wait(lock);
      }

    • 能自动处理虚假唤醒,推荐使用此版本。
  • notify_one()

    • 唤醒一个正在等待此条件变量的线程(如果有多个在等待,具体唤醒哪个不确定)。
    • 通常用于只有一个线程能处理被满足的条件时。
  • notify_all()

    • 唤醒所有正在等待此条件变量的线程。
    • 通常用于多个线程都能处理被满足的条件时。
  • wait_for / wait_until

    • 带超时功能的等待。允许线程在指定时间段内或被通知时唤醒,避免永久阻塞。
  • 🧠 三、使用模式(生产者-消费者示例)

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

    std::mutex mtx;
    std::condition_variable cv;
    std::queue<int> data_queue;
    bool done = false; // 标记是否所有数据生产完毕

    // 生产者线程
    void producer() {
    for (int i = 0; i < 10; ++i) {
    {
    std::lock_guard<std::mutex> lk(mtx);
    data_queue.push(i);
    std::cout << "Produced: " << i << std::endl;
    } // lock_guard 离开作用域自动解锁
    cv.notify_one(); // 通知一个消费者
    }
    {
    std::lock_guard<std::mutex> lk(mtx);
    done = true; // 设置生产完成标志
    }
    cv.notify_all(); // 通知所有消费者可能结束
    }

    // 消费者线程
    void consumer() {
    while (true) {
    std::unique_lock<std::mutex> lk(mtx); // 必须用 unique_lock,因为 wait 会解锁
    // 等待条件:队列不为空 或 生产者已结束
    cv.wait(lk, []{ return !data_queue.empty() || done; });

    // 检查是否结束且队列为空
    if (done && data_queue.empty()) {
    break;
    }

    // 处理数据
    if (!data_queue.empty()) {
    int data = data_queue.front();
    data_queue.pop();
    std::cout << "Consumed: " << data << " by thread " << std::this_thread::get_id() << std::endl;
    }
    lk.unlock(); // 处理完数据后可以提前解锁
    // 模拟处理耗时
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    }

    int main() {
    std::thread prod(producer);
    std::thread cons1(consumer);
    std::thread cons2(consumer);

    prod.join();
    cons1.join();
    cons2.join();

    return 0;
    }

    ⚠️ 四、重要注意事项

  • 与锁配合:wait必须配合std::unique_lock<std::mutex>使用,因为它需要在等待期间解锁并在唤醒后重新加锁。
  • 谓词检查:强烈建议使用带谓词的wait版本,它自动处理虚假唤醒并保证条件真正成立。
  • 锁的作用域:
    • 在调用notify_one()或notify_all()时,不强制要求持有锁。但持有锁时通知可以避免一些竞态条件(例如,在检查条件后、开始等待前发生通知)。
    • 在修改条件(如设置done或修改data_queue)时,必须持有锁。
  • 避免通知丢失:确保在修改条件后及时通知。如果修改条件后没有通知,等待线程可能永远阻塞。
  • notify_one vs notify_all:根据场景选择。notify_one效率更高,但可能只唤醒一个不满足条件的线程(如果谓词复杂);notify_all更简单但可能引起不必要的唤醒(惊群效应)。
  • 💡 五、总结

    std::condition_variable是实现线程间高效协作(特别是生产者-消费者模型)的关键工具。正确使用它需要理解其与互斥锁的配合、等待谓词的检查以及通知的时机。务必使用带谓词的wait来避免虚假唤醒问题。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » C++11条件变量:线程同步核心技巧
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!