生产者和消费者模型图
模型解释:
生产者消费者模型本质就是通过一个容器来解决两者之间的强耦合关系,生产消费之间不直接通讯,而是通过阻塞队列。生产者生产完数据之后直接交给阻塞队列,消费者需要数据的时候也是直接从阻塞队列中拿。以此来平衡生产者和消费者之间的处理能力。而这个阻塞队列,就是用来给生产者和消费者解耦的。
优点:
- 解耦(平衡生产者消费者之间的处理能力)
- 支持并发
- 支持忙闲不均(加入生产者生产的太快,消费者速度跟不上,那么就可以通过生产者消费者模型,来阻塞生产者生产)
关系:
- 生产者和生产者,消费者和消费者之间都是互斥关系。
- 生产者和消费者之间的关系是互斥且同步。
- 最少有一个生产者、消费者和一个交易场所。
基于BlockingQueue的生产者消费者模型
//#################cp.hpp##########################
//hpp是C++的头文件,这个头文件有一个特点就是可以把实现也写在里边
//条件编译
#ifndef __CP_HPP__ //如果没有定义cp.hpp
#define __CP_HPP__ //定义cp.hpp
#include<iostream>
#include<queue>
#include<pthread.h>
#include<time.h>
#include<stdlib.h>
#include<unistd.h>
class BlockQueue{
private:
std::queue<int> bq;
int cap;
pthread_mutex_t lock;
pthread_cond_t cond_p; //队列为空时使用这个信号
pthread_cond_t cond_c; //队列满的时候使用这个信号
private:
void LockQueue()
{
pthread_mutex_lock(&lock);
}
void UnlockQueue()
{
pthread_mutex_unlock(&lock);
}
void ProductWait()
{
pthread_cond_wait(&cond_p,&lock);
}
void ConsumeWait()
{
pthread_cond_wait(&cond_c,&lock);
}
bool IsFull()
{
return bq.size()==cap?true:false;
//size表示当前队列中,有效元素的个数
}
bool IsEmpty()
{
return bq.size()==0?true:false;
}
void SignalConsume()
{
pthread_cond_signal(&cond_c);
}
void SignalProduct()
{
pthread_cond_signal(&cond_p);
}
public:
BlockQueue(int cap_):cap(cap_) //构造函数,参数列表把cap放进去。
// 上句的意思相当于:
// BlockQueue(int cap_)
// {
// cap=cap_;
// }
{
pthread_mutex_init(&lock,NULL);
pthread_cond_init(&cond_p,NULL);
pthread_cond_init(&cond_c,NULL);
}
void PushData(const int &data)
{
LockQueue();
while(IsFull())
{
SignalConsume();
ProductWait();
}
bq.push(data);
UnlockQueue();
}
void PopData(int &data)
{
LockQueue();
while(IsEmpty())
{
SignalProduct();
ConsumeWait();
}
//bq.front拿出头部信息,STL中的queue使用pop时,数据会直接被丢弃,因此未使用pop
data=bq.front();
bq.pop();
UnlockQueue();
}
~BlockQueue()
{
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond_c);
pthread_cond_destroy(&cond_p);
}
};
#endif
//########################cp.cc#########################
#include"cp.hpp"
using namespace std;
const int num=32;
//生产者和消费者两个线程一个不断的Push数据,一个不断的Pop数据
void* consume_routinue(void* arg)
{
int data=0;
BlockQueue *bqp=(BlockQueue*)arg;
for(;;)
{
bqp->PopData(data);
cout<<"consume done,data is :"<<data<<endl;
//上一句的data没有加*是因为C++的引用。
}
}
void* product_routinue(void* arg)
{
BlockQueue *bqp=(BlockQueue*)arg;
srand((unsigned long)time(NULL));
//利用系统时间产生一系列的随机数。
//srand原型srand(unsinged seed)
for(;;)
{
int data=rand()%100+1;
//取余100再加1的结果是产生1-100的随机数
bqp->PushData(data);
cout<<"Product is done,data is: "<<data<<endl;
sleep(1);
}
}
int main()
{
BlockQueue *bqp=new BlockQueue(num);
//C++中的new不需要判断,因为在new失败的时候,程序会抛出异常。
pthread_t c,p;
pthread_create(&c,NULL,consume_routinue,(void*)bqp);
pthread_create(&p,NULL,product_routinue,(void*)bqp);
pthread_join(&c,NULL);
pthread_join(&p,NULL);
delete(bqp);
return 0;
}
//#######################Makefile##########################
cp:cp.cc
g++ -o $@ $^ -lpthread -fpermissive
.PHONT:clean
clean:
rm -f cp