结合这篇文章使用:基于阻塞队列的生产者消费者模型
一、概述
生产者消费者模型遵循的123原则,即
一个交互空间:一种数据结构
两种对象:生产者与消费者
三种关系:生产者与生产者互斥,消费者与消费者互斥,生产者与消费者互斥和同步
二、基于环形队列的生产者消费者模型的优点
基于环形队列的生产者消费者模型较之基于阻塞队列的生产者消费者模型,优点在于,所以可以支持生产者线程和消费者线程并发,。原因是生产者和消费者有各自的资源,并且提前申请各自的信号量,保证了各自的临界资源一定就绪;而阻塞队列共用一份资源,不管是生产者还是消费者访问必须都申请同一把锁,所以只能生产者和消费者线程都只能串行访问临界资源;
三、如何选择两种模型
如何选择生产者和消费者模型呢?资源整体使用就选择阻塞队列,分块使用就选择环形队列。
四、多生产者多消费者的意义
在环形队列中多生产者多消费者并不是比单生产者单消费者给交易场所生产数据或从交易场所消费数据的效率更高,毕竟生产者线程给交易场所放入数据和消费者在交易场所消费数据还需要各自多加一把锁(反而会降低生产消费的效率),而是在生成和处理数据时可以多线程并发处理,产生大量数据和处理大量数据。
五、代码
main.cc
#include <pthread.h>
#include<sys/types.h>
#include"RingQueue.hpp"
#include"task.hpp"
void* pRoutine(void* args)
{
RingQueue<Task>* rq=static_cast<RingQueue<Task>*> (args);
string opers="+-*/%";
while(true)
{
sleep(1);
int x=rand()%7;
int y=rand()%4;
char op=opers[rand()% opers.size()];
Task task(x,y,op);
rq->push(task);
cout<<"线程ID : "<<pthread_self() <<" ,productor done: "<<task.formatArg()<<endl;
}
}
void* cRoutine(void* args)
{
//得把生产消费的数据结构指针传给新创建的线程;生产者和消费者必须看到同一份空间
RingQueue<Task>* rq=static_cast<RingQueue<Task>*> (args);
while(true)
{
//消费环形队列里面的数据
Task task;
rq->pop(&task);
task();
cout<<"线程ID : "<<pthread_self() <<" ,consumer done: "<<task.formatRes()<<endl;
}
}
int main()
{
srand(time(nullptr)^getpid());
RingQueue<Task> * rq=new RingQueue<Task>(10);
// //单生产单消费
// pthread_t p,c;
// pthread_create(&p,nullptr,pRoutine,rq);
// pthread_create(&c,nullptr,cRoutine,rq);
// //等待线程结束
// pthread_join(p,nullptr);
// pthread_join(c,nullptr);
//多生产多消费(6个生产者线程,5个消费者线程)
pthread_t p[6],c[6];
for(int i=0;i<6;++i)
pthread_create(&p[i],nullptr,pRoutine,rq);
for(int i=0;i<5;++i)
pthread_create(&c[i],nullptr,cRoutine,rq);
for(int i=0;i<6;++i)
pthread_join(p[i],nullptr);
for(int i=0;i<5;++i)
pthread_join(c[i],nullptr);
delete rq;
return 0;
}
RingQueue.hpp
#pragma once
#include<iostream>
#include<semaphore.h>
#include<ctime>
#include<unistd.h>
#include<vector>
using namespace std;
const int N=5;
template<class T>
class RingQueue
{
void P(sem_t* sem)
{
sem_wait(sem);
}
void V(sem_t* sem)
{
sem_post(sem);
}
void Lock(pthread_mutex_t& mutex)
{
pthread_mutex_lock(&mutex);
}
void UnLock(pthread_mutex_t& mutex)
{
pthread_mutex_unlock(&mutex);
}
public:
RingQueue(int num=N):_ring(num),_cup(num),_consumer_step(0),_productor_step(0)
{
sem_init(&_data_sem,0,0);
sem_init(&_space_sem,0,_cup);
pthread_mutex_init(&_c_mutex,nullptr);
pthread_mutex_init(&_p_mutex,nullptr);
}
void push(const T& in)
{
P(&_space_sem);
Lock(_p_mutex);
_ring[_productor_step++]=in;
_productor_step %= _cup;
//消费者信号量加一;(数据)
V(&_data_sem);
UnLock(_p_mutex);
}
void pop(T* out)
{
P(&_data_sem);
Lock(_c_mutex);
*out=_ring[_consumer_step++];
_consumer_step %= _cup;
//生产者信号量加一(空间)
V(&_space_sem);
UnLock(_c_mutex);
}
~RingQueue()
{
sem_destroy(&_data_sem);
sem_destroy(&_space_sem);
pthread_mutex_destroy(&_c_mutex);
pthread_mutex_destroy(&_p_mutex);
}
private:
vector<T> _ring;//数组模拟环形队列
int _cup;//容量
sem_t _data_sem;//消费者信号量
sem_t _space_sem;//生产者信号量
int _consumer_step;//消费者下标
int _productor_step;//生产者下标
// 单生产和单消费不存在竞争问题,只要有信号量即可;但是多生产和多消费的线程,可能都申请到了信号量,但是都在竞争同一块资源,无法保证原子性;
pthread_mutex_t _c_mutex;
pthread_mutex_t _p_mutex;
};
task.hpp
#pragma once
#include<string>
#include<iostream>
class Task
{
public:
Task(){}
Task(int x,int y,char op):_x(x),_y(y),_op(op),_result(0),_exitCode(0) {}
int operator()()
{
switch(_op)
{
case '+':
_result=_x+_y;
break;
case '-':
_result=_x-_y;
break;
case '*':
_result=_x*_y;
break;
case '/':
{
if(_y==0)
_exitCode=-1;
else
_result=_x/_y;
}
break;
case '%':
{
if(_y==0)
_exitCode=-2;
else
_result=_x%_y;
}
break;
default:
break;
}
}
std::string formatArg()
{
return std::to_string(_x) + _op + std::to_string(_y) + "=?";
}
std::string formatRes()
{
return std::to_string(_result) + "(" + std::to_string(_exitCode)+ ")";
}
~Task() {}
private:
int _x;
int _y;
char _op;
int _result;
int _exitCode;
};
makefile
ringQueue:main.cc
g++ $^ -o $@ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -f ringQueue