今天一次讲明白C++条件变量

在C++中,std::condition_variable 条件变量是一个同步原语,它允许一个或多个线程在某个条件成立时,被另一个线程唤醒。std::condition_variable 条件变量通常与互斥锁(std::mutex)一起使用,以保护共享数据和同步线程之间的通信。

条件变量的使用场景

条件变量主要用于以下场景:

  1. 等待/通知模型:一个或多个线程等待某个条件成立,另一个线程在条件成立时通知等待的线程。
  2. 生产者/消费者问题:生产者线程生产数据后通知消费者线程,消费者线程等待生产者生产数据。

条件变量的基本使用步骤

  1. 定义互斥锁和条件变量:首先,你需要定义一个互斥锁(例如std::mutex)和一个 std::condition_variable 条件变量。
  2. 锁定互斥锁:在调用 std::condition_variable 条件变量的 wait 或 wait_for 等函数之前,必须先锁定互斥锁。
  3. 调用条件变量的等待函数:调用 wait、wait_for 或 wait_until 等函数来等待条件成立。这些函数会释放互斥锁,使其他线程可以进入临界区,并在条件变量上等待。当其他线程调用 std::condition_variable 条件变量的 notify_one 或 notify_all 来唤醒等待的线程时,等待的线程会重新获取互斥锁并继续执行。
  4. 检查条件:在 wait、wait_for 或 wait_until 返回后,通常需要重新检查条件是否满足,因为可能存在虚假唤醒(spurious wakeup)的情况。
  5. 解锁互斥锁:在离开临界区时,需要解锁互斥锁。

使用示例

以下是一个简单的生产者/消费者问题的示例,展示了 std::condition_variable 条件变量的使用:

#include <iostream>  
#include <thread>  
#include <mutex>  
#include <condition_variable>  
#include <queue>  
  
std::mutex g_mtx;  
std::condition_variable g_cv;  
std::queue<int> g_queue;  
bool g_done = false;  
  
// 生产者函数
void producer(int id) {  
    for (int i = 0; i < 10; ++i) {  
        std::unique_lock<std::mutex> lck(g_mtx);  
        g_queue.push(id * 10 + i);  
        std::cout << "Produced: " << id * 10 + i << std::endl;  
        lck.unlock();  
        g_cv.notify_one(); // 通知一个等待的线程  
    }  
  
    {  
        std::lock_guard<std::mutex> lck(g_mtx);  
        g_done = true;  
    }  
    g_cv.notify_all(); // 通知所有等待的线程  
}  
  
// 消费者函数
void consumer() {  
    std::unique_lock<std::mutex> lck(g_mtx);
    while (!g_done && g_queue.empty()) {  
        g_cv.wait(lck); // 等待条件成立  
    }  
  
    while (!g_queue.empty()) {  
        int val = g_queue.front();  
        g_queue.pop();  
        std::cout << "Consumed: " << val << std::endl;  
        lck.unlock();  
  
        // 模拟耗时操作,等待100毫秒  
        std::this_thread::sleep_for(std::chrono::milliseconds(100));  
  
        lck.lock();  
    }  
}  
  
int main() {  
    std::thread producers[2];      // 生产者线程池
    std::thread consumers[5];      // 消费者线程池
  
    // 创建生产者  
    for (int i = 0; i < 2; ++i) {  
        producers[i] = std::thread(producer, i);  
    }  
  
    // 创建消费者  
    for (int i = 0; i < 5; ++i) {  
        consumers[i] = std::thread(consumer);  
    }  
  
    // 等待生产者完成  
    for (auto& t : producers) {  
        t.join();  
    }  
  
    // 唤醒所有等待的消费者(尽管这里可能不是必需的,因为done标志已经设置)  
    g_cv.notify_all();  
  
    // 等待消费者完成  
    for (auto& t : consumers) {  
        t.join();  
    }  
  
    return 0;  
}

在这个例子中,生产者和消费者线程使用条件变量(g_cv)和互斥锁(g_mtx)来同步对共享队列(g_queue)的访问。生产者生产数据后,通过调用notify_one来唤醒一个等待的消费者线程。当所有生产者都完成生产后,它们将done标志设置为true,并通过调用`notify_all 来唤醒所有的消费者线程。

-End-

#想了解更多精彩内容,关注下方公众号。

本人小杨哥:

超20年C++开发经验,独立软件开发;著名开源产品高并发C++应用服务器MYCP作者;开源企业即时通讯软件Entboost首席架构师;开发有【WordBN字远笔记】等共享软件产品。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天恩软件工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值