1、基础概念:
1.1、什么是生产者-消费者模式?
比如有两个线程A和B,它们共享一个固定大小的数组,A线程产生数据放入数组,B线程从数组中取出数据进行计算,那么这里其实就是一个生产者和消费者的模式,A相当于生产者,B相当于消费者。
1.2、为什么要使用生产者消费者模式?
在多线程开发中,如果生产者生产数据的速度很快,而消费者消费数据的速度很慢,那么生产者就必须等待消费者消费完了数据才能够继续生产数据,因为生产那么多也没有地方放啊;同理如果消费者的速度大于生产者那么消费者就会经常处理等待状态,所以为了达到生产者和消费者生产数据和消费数据之间的平衡,那么就需要一个缓冲区用来存储生产者生产的数据,所以就引入了生产者-消费者模式。
2、必备语法知识:关于线程阻塞、唤醒相关的函数
2.1、条件变量 std::condition_variable_any(Condition variable (any lock)) 与std::condition_variable
解释:两个条件变量作用相同,差别只在std::condition_variable的wait函数只能接受unique_lock<mutex>做参数,而std::condition_variable_any可接受任意锁对象类型作为参数。
2.2、condition_variable_any::wait
作用: 阻塞当前线程,直到该条件变量被唤醒
解释: 当前加锁的线程,在调用wait函数后,阻塞当前线程,同时自动调用lck.unlock()释放锁(供其他线程获取锁),直到被唤醒。
详细解释: http://www.cplusplus.com/reference/condition_variable/condition_variable_any/wait/
2.3、condition_variable_any::wait_for
作用: 与函数wait一致,但有超时时间,超时后自动唤醒
2.4、condition_variable_any::notify_all
作用: 唤醒当前等待该条件变量的所有线程
2.5、condition_variable_any::notify_one
作用: 唤醒一个等待该条件变量的线程
具体使用方式见生产者消费者用例:
生产者消费者官方案例:
// condition_variable::notify_one
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable produce,consume;
int cargo = 0; // shared value by producers and consumers
void consumer () {
std::unique_lock<std::mutex> lck(mtx);
while (cargo==0) consume.wait(lck);
std::cout << cargo << '\n';
cargo=0;
produce.notify_one();
}
void producer (int id) {
std::unique_lock<std::mutex> lck(mtx);
while (cargo!=0) produce.wait(lck);
cargo = id;
consume.notify_one();
}
int main ()
{
std::thread consumers[10],producers[10];
// spawn 10 consumers and 10 producers:
for (int i=0; i<10; ++i) {
consumers[i] = std::thread(consumer);
producers[i] = std::thread(producer,i+1);
}
// join them back:
for (int i=0; i<10; ++i) {
producers[i].join();
consumers[i].join();
}
return 0;
}
3、生产者消费者加解锁、阻塞唤醒机制
- 容器中数据状态的一致性:为了防止多个生产、消费者同时访问容器中数据,因此对于该容器的任何访问都需要加锁。
- 生产者加锁,put数据后,需要释放锁,同时唤醒消费者线程,若容器已满,则同时阻塞自己(当前生产线程)。
- 消费者加锁,take数据后,需要释放锁,同时唤醒生产者线程,若容器为空,则同时阻塞自己(当前消费线程)。
3.1、生产者消费者代码用例:
- http://www.cplusplus.com/reference/condition_variable/condition_variable/notify_one/
- https://zhuanlan.zhihu.com/p/83812690
4、总结:
1、条件变量让线程阻塞时回自动释放锁。
2、阻塞线程被唤醒时自动获取锁。
参考资料:
1、基础概念及图文理解参考
2、函数语法