<condition_variable> 是 C++ 标准库中用于线程同步的高级工具,主要用于在线程间协调共享数据的状态变化。借助条件变量(condition variable),线程可以在等待特定条件满足时阻塞自身,并在条件满足后被通知唤醒,从而实现高效的线程协作。


目录

  1. <condition_variable> 头文件简介
  2. 条件变量基础
  3. 使用条件变量进行线程同步
  4. 示例:生产者-消费者模型
  5. 常见注意事项
  6. 结论

1. <condition_variable> 头文件简介

条件变量用于线程间的通信,其主要思想是允许一个或多个线程等待直到另一个线程发送信号。通过使用 <condition_variable>,可以避免忙等待(busy waiting),提高程序的效率和响应性。要使用条件变量,需要包含该头文件:

#include <condition_variable>


2. 条件变量基础

2.1 std::condition_variable

  • 功能:用于线程间等待和通知。常与互斥量(如 std::mutex)配合使用,保护共享数据的访问。
  • 典型用法:线程调用 wait() 函数等待条件满足,而其他线程调用 notify_one()notify_all() 发出通知。

2.2 std::condition_variable_any

  • 功能:与 std::condition_variable 类似,但可以与任何满足 Lockable 概念的锁配合使用,而不仅限于 std::unique_lock<std::mutex>

3. 使用条件变量进行线程同步

条件变量的常见操作主要有两类:等待和通知。

3.1 等待(wait)操作

等待操作通常与 std::unique_lock 一起使用。线程在等待之前会释放互斥量,等待被通知后,再重新获取锁继续执行。

基本用法示例:

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

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void waiting_thread() {
    std::unique_lock<std::mutex> lock(mtx);
    // 等待直到 ready 为 true
    cv.wait(lock, [] { return ready; });
    std::cout << "Condition satisfied, proceeding..." << std::endl;
}

在上述代码中,线程调用 cv.wait(lock, [] { return ready; });,会释放 mtx 并等待通知。当其他线程修改 readytrue 并发出通知时,该线程将重新获取锁并继续执行。

3.2 通知(notify)操作

通知操作用于唤醒等待的线程,主要有两种:

  • notify_one():唤醒一个等待线程。
  • notify_all():唤醒所有等待线程。

通知示例:

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

extern bool ready;
extern std::mutex mtx;
extern std::condition_variable cv;

void signaling_thread() {
    {
        std::lock_guard<std::mutex> lock(mtx);
        ready = true;  // 修改共享数据状态
    }
    cv.notify_all();  // 通知所有等待线程
    std::cout << "Notified waiting threads." << std::endl;
}


4. 示例:生产者-消费者模型

以下示例展示了如何使用条件变量实现简单的生产者-消费者模型。生产者生成数据并通知消费者,而消费者等待数据的产生。

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

std::queue<int> dataQueue;
std::mutex mtx;
std::condition_variable cv;
bool finished = false;

void producer() {
    for (int i = 1; i <= 10; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            dataQueue.push(i);
            std::cout << "Produced: " << i << std::endl;
        }
        cv.notify_one(); // 通知等待的消费者
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    {
        std::lock_guard<std::mutex> lock(mtx);
        finished = true;
    }
    cv.notify_all(); // 通知所有消费者生产结束
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return !dataQueue.empty() || finished; });
        
        while (!dataQueue.empty()) {
            int data = dataQueue.front();
            dataQueue.pop();
            lock.unlock();  // 在处理数据前释放锁以提高并发性
            std::cout << "Consumed: " << data << std::endl;
            lock.lock();
        }
        
        if (finished && dataQueue.empty())
            break;
    }
}

int main() {
    std::thread prod(producer);
    std::thread cons(consumer);
    
    prod.join();
    cons.join();
    
    std::cout << "Processing complete." << std::endl;
    return 0;
}

在这个示例中:

  • 生产者将数据压入队列,并在每次生产后通知消费者。
  • 消费者在等待队列非空或生产结束的条件下被唤醒,然后取出数据进行处理。

5. 常见注意事项

  • 保护共享数据:在等待和通知过程中,必须使用互斥量保护共享数据的访问,防止数据竞争。
  • 避免虚假唤醒:总是将 wait() 与条件判断结合使用,使用带有谓词版本的 wait(lock, predicate) 可以自动处理虚假唤醒问题。
  • 通知后释放锁:在调用 notify_one()notify_all() 前修改共享数据后,应尽快释放锁,确保等待线程能尽快获得锁继续执行。

6. 结论

条件变量是 C++ 多线程编程中实现线程间协调的重要机制。通过 <condition_variable>,你可以编写高效且响应迅速的多线程应用程序,避免忙等待,并实现线程安全的等待和通知机制。掌握条件变量的使用将帮助你构建更加健壮和高效的并发程序。

推荐阅读: