C++条件变量

本文展示了使用C++实现的一个生产者消费者问题的示例代码。通过std::queue作为缓冲区,利用std::mutex互斥锁和std::condition_variable条件变量来保证线程安全,使得生产者线程在缓冲区满时等待,消费者线程在缓冲区空时等待,实现了数据生产和消费的同步。
摘要由CSDN通过智能技术生成
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>

const int MaxBufferSize = 10;
std::queue<int> buffer;
std::mutex mtx;
std::condition_variable cv;

void producer() {
    for (int i = 1; i <= 20; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(200));  // 模拟生产时间

        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [] { return buffer.size() < MaxBufferSize; });  // 等待缓冲区有空闲位置

        buffer.push(i);
        std::cout << "Produced: " << i << std::endl;

        cv.notify_all();  // 通知消费者有新数据可用
    }
}

void consumer() {
    for (int i = 1; i <= 20; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(500));  // 模拟消费时间

        std::unique_lock<std::mutex> lock(mtx);
/*
以下来自chatgpt:
首先,它会获取一个互斥锁 (由调用 cv.wait 时传入的 std::unique_lock<std::mutex> 对象管理)。这个互斥锁用于在等待期间保护共享资源,以避免多个线程同时访问和修改。

然后,它会检查条件,即在等待期间判断是否满足特定条件的函数。条件是通过一个可调用的谓词 (函数、函数对象或 Lambda 表达式) 指定的,它返回一个 bool 值,表示条件是否满足。如果条件不满足,线程将被阻塞。

如果条件不满足,cv.wait 会释放之前获取的互斥锁,并将当前线程置于等待状态,直到收到通知。

当收到通知时,cv.wait 会再次获取互斥锁,并重新检查条件。如果条件满足,线程将继续执行;否则,它会继续等待通知。

在等待期间,cv.wait 会暂时释放互斥锁,允许其他线程访问共享资源并修改其状态。这种释放和重新获取锁的机制可以确保线程安全,并允许多个线程在合适的时机进行并发操作。


*/

/*
以下来自我自己:
    wait奇特的点在于:
        在即将执行到wait的时候,线程是持有互斥锁的,这意味着其他线程(也就是生产者线程)都没有锁。这没有问题。
        但是在wait内部,他在发现条件不满足的时候,【会释放这个互斥锁】。这其实破坏了lock的raii概念,也就是说,lock变量并没有销毁,但锁却释放了。
            1.首先,为什么要释放这个锁?因为虽然我们wait语句的前面的语句已经获取到了锁,但是我们发现我们并不能操作共享数据,所以我们占着缓冲区不给别人用时不合理的,我们需要释放。
                那么简单的想法就是释放锁,执行unlock即可。所以代码是:
                    lock.lock();
                    while(is_empty) {
                        lock.unlock();// 释放锁。
                        // 企图让别人往里面塞数据。
                        lock.lock();// 尝试再获取锁。
                        // 然后再判断是否有了数据。
                    }
                    comsume something;  // 此时是拿到锁的,可以放心消费了

                但是这种while的实现的问题就是空循环太多,你在while里面的unlock和lock之间的时间可能很短,别人根本来不及插入数据,这就导致你一直在那里空转。
                所以我们需要一种通知机制,unlock后,执行一个等待操作(逻辑上必须等待,因为如果没有人往里面插了数据的话,即使获取到了锁,也注定是一次空转;相反,如果确实有人往里面插入了数据,那么我们尝试获取锁是一次有意义的操作:有可能我们获取到了锁,且数据还没有被别人消费掉,当然也有可能在获取锁期间数据又被别人消费了,那么这又是一次白白的唤醒操作。),这个等待操作一定会阻塞,直到别人通知我。伪代码如下:
                    lock.lock();
                    while(is_empty) {
                        lock.unlock();
                        wait_until_somebody_notify_me();
                        lock.lock();
                    }
                    consume something;
                所以我们看到,唯一的区别就是,之前这是企图让别人往里面塞数据,现在变成了:明确等待别人通知我塞了数据,我猜尝试获取锁,然后判断。

                那么核心机制其实是wait_until_somebody_notify_me();这个语句内部是如何实现的。实际中可能有多个线程阻塞在这一句。
*/
        cv.wait(lock, [] { return !buffer.empty(); });  // 等待缓冲区有数据可消费

        int data = buffer.front();
        buffer.pop();
        std::cout << "Consumed: " << data << std::endl;

        cv.notify_all();  // 通知生产者有空闲位置
    }
}

int main() {
    std::thread producerThread(producer);
    std::thread consumerThread(consumer);

    producerThread.join();
    consumerThread.join();

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值