1. 概述
生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。
2. 实现细节
- 具体的实现逻辑是构建一个queue来存储生产的数据,queue不满时可以生产,不空时可以消费。
- 对于这个队列,采用阻塞队列的实现思路。
- 先实现构造函数,初始化一个unique_lock供condition_variable使用。
- 如何在类里面使用unique_lock等需要初始化,并且初始化会加锁的对象。这要研究下。我的理解是构造列表初始化,然后函数体里unlock。
- 对于条件变量,申请两个,分别控制consumer和producer。
- 然后就是入和出队列的细节。
- 首先加锁。
- 循环判断一下目前的队列情况,对于各自的特殊情况(队满和队空)进行处理。
- 唤醒一个线程来处理特殊情况。
- 等待处理完毕。
- 处理入和出队列操作。
- 最后释放锁。
- 对于输出cout可能因为多线程紊乱的问题,加入了临界区。不过据说因为cout缓存问题,还是可能出错,没试验出来。
单生产者-单消费者
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <unistd.h>
#define PRODUCT_SIZE 20
#define CUSTOM_SIZE 1
#define MAX_SIZE 10
using namespace std;
mutex mut; //全局互斥变量
condition_variable con;
queue<int> que;
int cnt = 0;
void Productor() {
while(true) {
sleep(10);
// 获得锁 声明周期内,它所管理的锁对象会一直保持上锁状态
// lock局部变量 离开作用域调用自己的析构函数 自动释放
std::unique_lock<std::mutex> lock(mut);
while(que.size() > MAX_SIZE) {
con.wait(lock); // 阻塞 当队列为满得时候,生产数据的线程就不再生产数据
}
int data = cnt++;
que.push(data);
cout << this_thread::get_id() << "生产了产品: " << data << endl;
con.notify_all();
}
}
void Customer() {
while(true) {
std::unique_lock <std::mutex> lock(mut);
while(que.empty()) {
con.wait(lock); // 当队列为空得时候,消耗数据的线程就不再消耗数据。
}
cout << this_thread::get_id() << " 消费产品: " << que.front() << endl;
que.pop();
con.notify_all(); // 唤醒所有线程
}
}
int main() {
vector<thread> threadPoll;
for(int i=0; i<PRODUCT_SIZE; ++i) {
threadPoll.push_back(thread(Productor));
}
for(int i=0; i < CUSTOM_SIZE; i++) {
threadPoll.push_back(thread(Customer));
}
for(int i=0; i<PRODUCT_SIZE+CUSTOM_SIZE; ++i) {
threadPoll[i].join();
}
return 0;
}