Linux——生产者/消费者模型(条件变量、信号量)

生产者消费者模型

简述:有时候对于临界资源的访问是有限制条件的,举例说明

打印机:只有有任务来了才可以进行打印;另一方面,只有任务队列有空间了任务才能被添加。

一.条件变量

流程:封装了一个线程安全的任务队列

   1.定义变量:queue(保存任务的信息)、_capacity(队列中的最大节点数目)、_mutex(对临界资源访问时要加锁)、_con(消费者)、_pro(生产者)

   2.实现任务入队和任务出队:入队时,加锁、判断队列是否已满(满则等待)、添加节点、唤醒消费者、解锁

     出队时:加锁、判断有无节点数据(无则等待)、拿到节点任务出队、唤醒生产者、解锁

   3.记得最后销毁所有变量:锁、条件变量

//使用条件变量实现生产者/消费者模型
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <mutex>
#include <queue>
using namespace std;

const int size = 4; //表示线程的数目
const int Max = 5;  //表示队列的最大容量

class BlockQueue{
  private:
    queue<int> _queue;        //假定数据都是int类型
    int _capacity;
    pthread_mutex_t _mutex;   //互斥锁
    pthread_cond_t _pro;
    pthread_cond_t _con;
  public:

    //1.初始化所有变量
    BlockQueue(int maxq = Max):_capacity(maxq){
      pthread_mutex_init(&_mutex, NULL);
      pthread_cond_init(&_pro, NULL);
      pthread_cond_init(&_con, NULL);
    }

    //2.入队
    bool QueuePush(int data){
      //先加锁,再判断是否已满
      pthread_mutex_lock(&_mutex);
      while(_queue.size() >= _capacity){
        //表示已经满了,就循环等待
        pthread_cond_wait(&_pro, &_mutex);
        //cout << "生产产品 " << _queue.size() << "等待中!!!" << endl;
      }
      //有空间,就直接进行入队操作
      _queue.push(data);
      //cout << "产品" << data << " 生产完毕" << endl;
      cout << "现在有 " << _queue.size() << "个数据"  << endl;
      //唤醒消费者,有数据可以处理了
      pthread_cond_signal(&_con);
      pthread_mutex_unlock(&_mutex);
      return true;
    }

    //3.任务出队
    bool QueuePop(int& data){
      pthread_mutex_lock(&_mutex);
      while(_queue.size() == 0){
        pthread_cond_wait(&_con, &_mutex);
        //cout << "消费产品" << _queue.size() << "等待中!!!" << endl;
      }
      data = _queue.front();
      _queue.pop();
      cout << "产品 " << _queue.size()+1 << "处理完毕" << endl;
      cout << "消费完后现在有 " << _queue.size() << "个数据" << endl;
      pthread_cond_signal(&_pro);
      pthread_mutex_unlock(&_mutex);
      return true;
    }

    //4.销毁所有变量
    ~BlockQueue(){
      pthread_mutex_destroy(&_mutex);
      pthread_cond_destroy(&_pro);
      pthread_cond_destroy(&_con);
    }
};

void* thr_pro(void* arg){
  BlockQueue* q = (BlockQueue*)arg;
  for(int i = 0;i < size;++i){
    cout << endl;
    cout << "生产者线程 " << i << "生产" << endl;
    q->QueuePush(i);
  }
  return NULL;
}

void* thr_con(void* arg){
  BlockQueue* q = (BlockQueue*)arg;
  int data;
  for(int i = 0;i < size;++i){
    //cout << "消费者线程 " << i << "消费" << endl;
    q->QueuePop(data);
    //cout << "消费者线程 " << i << "消费数据 " << data  << "完毕" << endl;
  }
  return NULL;
}

int main(){
  pthread_t pro[size], con[size];
  BlockQueue q;
  for(int i = 0;i < size;++i){
    pthread_create(&pro[i], NULL, thr_pro, (void*)&q);
  }
  for(int i = 0;i < size;++i){
    pthread_create(&con[i], NULL, thr_con, (void*)&q);
  }

  for(int i = 0;i < size;++i){
    pthread_join(pro[i], NULL);
  }
  for(int i = 0;i < size;++i){
    pthread_join(con[i], NULL);
  }
  return 0;
}

二.信号量

流程:同样是封装了一个线程安全的任务队列

   1.定义变量:_queue(vector类型)、_capacity、_write(读指针)、_read(写指针)、_lock(锁互斥量)、_empty_space(空闲空间)、_data_space(数据的数目)

   2.int sem_init(sem_t *sem, int pshared, unsigned int value);函数进行初始化其中pshared == 0表示在线程之间使用,!=0时表示在进程之间使用,value表示该变量的初始值;

     所以对于_lock来说只有0,1;开始就给1,否则无法访问到临界资源;

     对于_empty_space来说,初始值就是_capacity;

     对于_data_space来说,开始没有数据,所以是0.

   3.sem_wait() 和 sem_post()接口表示对某个信号量-1和+1操作

     如果-1后 < 0;就表示没有资源,等待;

     生产者生产数据后入队,空闲空间变少了,所以要-1;之后数据就会多一个,所以数据+1。

//使用POSIX信号量实现生产者/消费者模型
#include <iostream>
#include <thread>
#include <semaphore.h>
#include <vector>
using namespace std;

const int size = 4;   //最多4个线程
const int Max = 5;    //最多5个节点数据

class BlockQueue{
  private:
    vector<int> _queue;
    int _capacity;
    int _write;         //写指针
    int _read;          //读指针
    sem_t _lock;        //相当于锁互斥量
    sem_t _empty_space; //记录空闲空间
    sem_t _data_space;  //记录已经存放的数据量
  public:
    //1.初始化
    BlockQueue():_queue(Max),_capacity(Max),_write(0),_read(0){
      sem_init(&_lock, 0, 1);   //互斥锁的计数0/1
      sem_init(&_empty_space, 0, _capacity);
      sem_init(&_data_space, 0, 0);
    }

    //2.任务入队
    bool QueuePush(int val){
      sem_wait(&_empty_space);
      sem_wait(&_lock);
      _queue[_write] = val;
      _write = (_write + 1) % _capacity;
      sem_post(&_lock);
      sem_post(&_data_space);
      return true;
    }

    //3.任务出队
    bool QueuePop(int& val){
      sem_wait(&_data_space);
      sem_wait(&_lock);
      val = _queue[_read];
      _read = (_read + 1) % _capacity;
      sem_post(&_lock);
      sem_post(&_empty_space);
      return true;
    }

    //4.销毁
    ~BlockQueue(){
      sem_destroy(&_lock);
      sem_destroy(&_empty_space);
      sem_destroy(&_data_space);
    }
};

void thr_pro(BlockQueue* q){
  int val = 0;
  int i = 0;
  for(int i = 0;i < size;++i){
    q->QueuePush(val);
    cout << "生产者线程 " << i << "生产 " << val << "完毕!!!" << endl;
    ++val;
    //++i;
  }
}

void thr_con(BlockQueue* q){
  int val = 0;
  int i = 0;
  for(int i = 0;i < size;++i){
    q->QueuePop(val);
    cout << "消费者线程 " << i << "处理数据 " << val << "完毕!!!" << endl;
    //++i;
  }
}

int main(){
  BlockQueue q;
  vector<thread> pro(4);
  vector<thread> con(4);

  for(int i = 0;i < size;++i){
    pro[i] = thread(&thr_pro, &q);
  }

  for(int i = 0;i < size;++i){
    con[i] = thread(&thr_con, &q);
  }

  for(int i = 0;i < size;++i){
    pro[i].join();
    con[i].join();
  }

  return 0;
}

注意:

   1.条件变量需要搭配锁互斥量一起使用,而信号量则不用(本身的计数即可实现锁)

   2.C++中的thread库,参数只有入口函数和入口函数的参数即可,但是入口函数的结构与前者不同

        void* thr(void* arg);

        void thr(类型自己设置);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值