目录
生产者消费者模型
为何要使用生产者消费者模型
⽣产者消费者模式就是通过⼀个容器来解决⽣产者和消费者的强耦合问题。⽣产者和消费者彼此之间不直接通讯,⽽通过阻塞队列来进⾏通讯,所以⽣产者⽣产完数据之后不⽤等待消费者处理,直接扔给阻塞队列,消费者不找⽣产者要数据,⽽是直接从阻塞队列⾥取,阻塞队列就相当于⼀个缓冲区, 平衡了⽣产者和消费者的处理能⼒。这个阻塞队列就是⽤来给⽣产者和消费者解耦的。
生产者消费者模型总结: 321原则
3种关系:
- 生产者与生产者 : 互斥
- 消费者与消费者 : 互斥
- 生产者与消费者 :互斥与同步
2种角色: 消费者与生产者
生产者的职责: 1. 获取数据 2. 传输到交易场所
消费者的职责: 1.从交易场所拿数据 2.对数据进行处理
1个场所: 一个交易场所 ,交易场所是临界资源
生产者消费者模型优点
- 解耦
- ⽀持并发
- ⽀持忙闲不均
基于BlockingQueue的⽣产者消费者模型
BlockingQueue
在多线程编程中阻塞队列(Blocking Queue)是⼀种常⽤于实现⽣产者和消费者模型的数据结构。其与普通的队列区别在于,当队列为空时,从队列获取元素的操作将会被阻塞,直到队列中被放⼊了元素;当队列满时,往队列⾥存放元素的操作也会被阻塞,直到有元素被从队列中取出(以上的操作都是 基于不同的线程来说的,线程在对阻塞队列进程操作时会被阻塞)
C++ queue模拟阻塞队列的⽣产消费模型
- 为了便于理解,我们以单⽣产者,单消费者,来进⾏。
- 刚开始写,我们采⽤原始接⼝。
- 先写单⽣产,单消费。然后改成多⽣产,多消费(这⾥代码其实不变)
BlockQueue.hpp
详细代码(带注释)code/lesson31/2. BlockQueue/BlockQueue.hpp · whb-helloworld/112 - 码云 - 开源中国
#pragma once
#include<iostream>
#include<pthread.h>
#include<queue>
namespace bpModule
{
template<typename T>
class BlockQueue
{
private:
bool IsFull(){return _q.size() == _maxcap;}
bool IsEmpty(){return _q.empty();}
public:
BlockQueue(int cap):_maxcap(cap),_cwaitnum(0),_pwaitnum(0)
{
pthread_mutex_init(&_mutex, nullptr);
pthread_cond_init(&_producer_cond ,nullptr);
pthread_cond_init(&_consumer_cond ,nullptr);
}
void Equeue(const T& in)
{
pthread_mutex_lock(&_mutex);
while(IsFull()) //重要重要:防止伪唤醒,通常使用while ,因为伪唤醒时(条件不符合) ,用while可以使得伪唤醒的继续在while中,使其不能向下执行
{
_pwaitnum++;
pthread_cond_wait(&_producer_cond,&_mutex);//前面博客讲了
_pwaitnum--;
}
//生产
_q.push(in);
if(_cwaitnum)
{
pthread_cond_signal(&_consumer_cond);
}
pthread_mutex_unlock(&_mutex);
}
void Pop(T* out)
{
pthread_mutex_lock(&_mutex);
while(IsEmpty()) //防止伪唤醒,通常使用while
{
_cwaitnum++;
pthread_cond_wait(&_consumer_cond,&_mutex);
_cwaitnum--;
}
//消费
*out =_q.front();
_q.pop();
//叫醒对方
if(_pwaitnum)
{
pthread_cond_signal(&_producer_cond);
}
pthread_mutex_unlock(&_mutex);
}
~BlockQueue()
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_producer_cond);
pthread_cond_destroy(&_consumer_cond);
}
private:
std::queue<T> _q; // 保存数据的容器,临界资源
int _maxcap; //bp最大容量
pthread_mutex_t _mutex; //互斥
pthread_cond_t _producer_cond; //生产者条件变量
pthread_cond_t _consumer_cond; //消费者条件变量
int _cwaitnum;
int _pwaitnum;
};
}
main.cc
多生产多消费如何实现?
直接加线程数量即可,因为321都维护好了.
#include"BlockQueue.hpp"
#include<unistd.h>
using namespace bpModule;
//消费者
void* consumer(void* args)
{
BlockQueue<int>* bp =static_cast<BlockQueue<int>* >(args);
while(true)
{
sleep(2);
//1.从bp拿到数据
int data;
bp->Pop(&data);
//2.处理数据
printf("Consumer, 消费了一个数据: %d\n", data);
}
}
//生产者
void* producer(void* args)
{
BlockQueue<int>* bp =static_cast<BlockQueue<int>* >(args);
//1.获取数据
int data =10;
while(true)
{
sleep(1);
//2.传输数据
bp->Equeue(data);
printf("producter 生产了一个数据: %d\n", data);
data++;
}
}
单生产,单消费
int main()
{
BlockQueue<int>* bp =new BlockQueue<int>(5);
pthread_t c1,p1 ;
pthread_create(&c1 ,nullptr ,consumer,bp);
pthread_create(&p1 ,nullptr ,producer,bp);
pthread_join(c1,nullptr);
pthread_join(p1,nullptr);
delete bp;
return 0;
}
多生产,多消费
int main()
{
BlockQueue<int>* bp =new BlockQueue<int>(5);
pthread_t c1 ,c2 ,p1 ,p2 ,p3;
pthread_create(&c1 ,nullptr ,consumer,bp);
pthread_create(&c2 ,nullptr ,consumer,bp);
pthread_create(&p1 ,nullptr ,producer,bp);
pthread_create(&p2 ,nullptr ,producer,bp);
pthread_create(&p3 ,nullptr ,producer,bp);
pthread_join(c1,nullptr);
pthread_join(c2,nullptr);
pthread_join(p1,nullptr);
pthread_join(p2,nullptr);
pthread_join(p3,nullptr);
delete bp;
return 0;
}