生產者與消費者模型:功能+實現方式
一種場所,兩種角色,三種關係
功能: 解耦合,支持忙閒不均,支持併發
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;
}