#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;
}
C++条件变量
最新推荐文章于 2024-10-31 09:04:32 发布
本文展示了使用C++实现的一个生产者消费者问题的示例代码。通过std::queue作为缓冲区,利用std::mutex互斥锁和std::condition_variable条件变量来保证线程安全,使得生产者线程在缓冲区满时等待,消费者线程在缓冲区空时等待,实现了数据生产和消费的同步。
摘要由CSDN通过智能技术生成