生产者与消费者模型:功能+实现方式
一种场所,两种角色,三种关系
功能: 解耦合,支持忙闲不均,支持并发
stl–std::queue—非线程安全
- 生产者-生产者:互斥
- 消费者-消费者:互斥
- 生产者-消费者:同步+互斥
class BlockQueue
{
private:
std::queue<int> _queue
int _capacity;//队列的最大节点数量,达到阻塞
pthread_mutex_t mutex;
pthread_cond_t cond_pro; //生产者
pthread_cond_t cond_con; //消费者
}
QueuePush(int data);//集成线程安全的入队操作
QueuePop(int* data);//集成线程安全的出队操作
手撕生产者与消费者模型
实现线程安全的队列,对提供线程安全的数据入队和出队操作
创建线程,分别作为生产者与消费者数据入队或数据出队
#include<iostream>
#include<pthread.h>
#include<queue>
#define MAX_QUEUE 10
class BlockQueue
{
public:
BlockQueue(int cap=MAX_QUEUE):_capacity(cap)
{
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&cond_con,NULL);
pthread_cond_init(&cond_pro,NULL);
}
~BlockQueue()
{
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond_con);
pthread_cond_destroy(&cond_pro);
}
void QueuePush(int data)
{
QueueLock();
while(QueueIsFull())
{
ProWait();
}
_queue.push(data);
ConWakeUp();
QueueUnLock();
}
void QueuePop(int *data)
{
QueueLock();
while(QueueIsEmpty())
{
ConWait();
}
*data=_queue.front();//获取队列头节点
_queue.pop();//节点出队
ProWakeUp();
QueueUnLock();
}
private:
void QueueLock()//加锁
{
pthread_mutex_lock(&mutex);
}
void QueueUnLock()//解锁
{
pthread_mutex_unlock(&mutex);
}
void ConWait()//消费者等待
{
pthread_cond_wait(&cond_con,&mutex);
}
void ConWakeUp()//唤醒消费者
{
pthread_cond_signal(&cond_con);
}
void ProWait()//生产者等待
{
pthread_cond_wait(&cond_pro,&mutex);
}
void ProWakeUp()//唤醒生产者
{
pthread_cond_signal(&cond_pro);
}
bool QueueIsFull()//判断队列是否满了
{
return (_capacity == _queue.size());//获取队列中的节点数量
}
bool QueueIsEmpty()//判断队列是否为空
{
return _queue.empty();//判断队列是否有节点
}
private:
std::queue<int> _queue;//STL容器中的队列
int _capacity;//队列节点最大数量
//线程安全实现成员
pthread_mutex_t mutex;
pthread_cond_t cond_pro;
pthread_cond_t cond_con;
};
void* thr_consumer(void *arg)
{
BlockQueue *q=(BlockQueue*) arg;
while(1)
{
int data;
q->QueuePop(&data);
std::cout<<"consumer get data:"<<data<<std::endl;
}
return NULL;
}
int i=0;
pthread_mutex_t mutex;
void* thr_productor(void *arg)
{
BlockQueue *q=(BlockQueue*) arg;
pthread_mutex_init(&mutex,NULL);
while(1)
{
pthread_mutex_lock(&mutex);
q->QueuePush(i++);
pthread_mutex_unlock(&mutex);
std::cout<<"productor put data:"<<i<<std::endl;
}
return NULL;
}
int main()
{
BlockQueue q;
pthread_t ctid[4],ptid[4];
int i,ret;
for(i=0;i<4;i++)
{
ret=pthread_create(&ctid[i],NULL,thr_consumer,(void*)&q);
if(ret!=0)
{
std::cout<<"pthread create error"<<std::endl;
return -1;
}
}
for(i=0;i<4;i++)
{
ret=pthread_create(&ptid[i],NULL,thr_productor,(void*)&q);
if(ret!=0)
{
std::cout<<"pthread create error"<<std::endl;
return -1;
}
}
for(i=0;i<4;i++)
{
pthread_join(ctid[i],NULL);
pthread_join(ptid[i],NULL);
}
return 0;
}
信号量:计数器+等待队列+等待与唤醒功能
通过自身的计数器实现条件判断,当条件满足时则直接返回并且计数-1,当条件不满时则阻塞
当产生资源后,通过信号的唤醒功能唤醒等待并且计数+1
信号量和条件变量实现同步的区别:
信号量的条件判断由自身来完成,而条件变量的条件判断由用户完成
信号量并不搭配互斥锁使用,而条件变量需要搭配互斥锁一起使用保护条件的改变
信号量操作步骤: | |
---|---|
1.sem_t | 定义 |
2.sem_init | 初始化 |
3.sem_wait | 计数判断是否阻塞 -1 |
4.sem_post | 计数+1,唤醒阻塞 |
5.sem_destory | 销毁 |
RingQueue
{
std::vector<int> _queue;//线性表
int _capacity;//队列最大节点数
int _step_write;//环形队列写指针
int _step_read;//环形队列读指针
sem_t _sem_lock;//用于互斥实现
sem_t _sem_space;//空闲空间计数
sem_t _sem_data;//数据资源计数
}
int sem_init(sem_t* sem,int pshared,unsigned int value);//初始化信号量初值
int sem_wait(sem_t* sem);//对计数进行判断,计数<=0则阻塞,否则立即返回流程继续计数-1
int sem_post(sem_t* sem);//对计数进行+1,并且唤醒等到的线程
- sem wait(sem_space) //空闲空间计数-1
- sem_post(sem_spcae) //空闲空间计数+1
- sem_post(sem data) //数据资源计数+1
- sem_wait(sem_data) //数据资源计数-1
/*使用信号量实现生产者与消费者模型*/
#include<iostream>
#include<queue>
#include<pthread.h>
#include<semaphore.h>
class RingQueue
{
public:
RingQueue(int cap=10):_capacity(cap),_queue(cap)
{
//int sem_init(sem_t *sem, int pshared, unsigned int value);
//sem:信号量变量
//pshared:0用于线程间同步与互斥,非0用于进程间同步与互斥
//value:信号量初值
sem_init(&_sem_lock,0,1);//互斥锁初始只给1
sem_init(&_sem_data,0,0);//初始数据资源计数为0
sem_init(&_sem_space,0,cap);//初始空间空间计数
}
~RingQueue()
{
//int sem_destroy(sem_t *sem);
sem_destroy(&_sem_lock);
sem_destroy(&_sem_data);
sem_destroy(&_sem_space);
}
void QueuePush(int data)
{
//ProWait();//空闲空间计数判断,是否有空闲空间,若有则返回,反之阻塞
//因为已经通过_sem_space的空闲空间计数知道是否有空闲空间
sem_wait(&_sem_space);//添加数据的时候,空闲空间计数-1
sem_wait(&_sem_lock);//锁计数初始为1,一旦进入-1==0
//if(_step_write+1==_step_read) sleep();
_queue[_step_write]=data;
_step_write=(_step_write+1)%_capacity;
//ConWakeUp();
sem_post(&_sem_lock);//操作完毕之后锁计数+1
sem_post(&_sem_data);//数据添加完毕之后,数据资源计数+1
}
void QueuePop(int *data)
{
sem_wait(&_sem_data);//取数据的时候,数据资源计数-1
sem_wait(&_sem_lock);//锁最好仅仅保护临界资源区
*data=_queue[_step_read];
_step_read=(_step_read+1)%_capacity;
sem_post(&_sem_lock);
sem_post(&_sem_space);//空闲取数据之后,空间空间计数+1
}
private:
std::vector<int> _queue;
int _capacity;
int _step_write;
int _step_read;
sem_t _sem_lock;//实现互斥锁
sem_t _sem_space;//空闲空间计数
sem_t _sem_data;//数据资源计数
};
void* thr_productor(void* arg)
{
RingQueue *q=(RingQueue*)arg;
int i=0;
while(1)
{
q->QueuePush(i);
std::cout<<"thread:"<<pthread_self()<<"put data:"<<i++<<std::endl;
}
return NULL;
}
void* thr_consumer(void* arg)
{
RingQueue *q=(RingQueue*)arg;
while(1)
{
int data;
q->QueuePop(&data);
std::cout<<"thread:"<<pthread_self()<<"get data:"<<data<<std::endl;
}
return NULL;
}
int main()
{
RingQueue q;
pthread_t ptid,ctid[4];
int i,ret;
ret=pthread_create(&ptid,NULL,thr_productor,(void*)&q);
if(ret!=0)
{
std::cout<<"thread create error"<<std::endl;
return -1;
}
for(i=0;i<4;i++)
{
ret=pthread_create(&ctid[i],NULL,thr_consumer,(void*)&q);
if(ret!=0)
{
std::cout<<"thread create error"<<std::endl;
return -1;
}
}
for(i=0;i<4;i++)
{
pthread_join(ctid[i],NULL);
}
pthread_join(ptid,NULL);
return 0;
}