生產者與消費者模型

生產者與消費者模型:功能+實現方式

一種場所,兩種角色,三種關係
功能: 解耦合,支持忙閒不均,支持併發
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,並且喚醒等到的線程

在這裏插入圖片描述

  1. sem wait(sem_space) //空閒空間計數-1
  2. sem_post(sem_spcae) //空閒空間計數+1
  3. sem_post(sem data) //數據資源計數+1
  4. 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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章