1.环形缓冲区的优势

在上篇博客基于阻塞队列的生产者消费者模型中我介绍了什么是生产者消费者模型以及生产者-消费者模式,还没了解的可以戳链接查看。
基于阻塞队列的实现,虽然简单,但是对内存分配性能要求较高的程序是不适用的,因为该方式在push的时候,可能会分配存储空间用于存储新元素;在pop时,可能会释放废弃元素的存储空间。如此频繁的对缓冲区进行分配和释放会大大浪费使用该模式的性能。

本篇博客是提高内存分配性能的一个解决方案-环形队列,在指定环形队列的大小后,我们可以将所有的push和pop操作都在这个固定的存储空间内进行,因此少掉了对于缓冲区元素所用存储空间的分配、释放。这是环形缓冲区的一个主要优势。

2.POSIX 信号量

这里要注意了,我们这次使用的是基于POSIX的信号量,千万不要误认为是SystemV的信号量,虽然两个东西不一样,但是他们的作用相同,他们都是用于保证同步的操作,达到无冲突访问共享资源的目的,但是POSIX信号量可以用于线程同步。

信号量本质上是一个记录临界资源数目的计数器,它有两个很重要的操作,一个是加操作,也被称为v操作,此操作会让信号量加一,另一个操作为减操作,也被称为p操作,此操作会让信号量值减一,接下来我们看看信号量的操作函数。

这里需要特别注意一点:虽然它只是是一个计数器,但是不能用全局变量代替,因为信号量的PV操作是具有原子性的,而我们自己定义的全局变量无法保证原子性。

信号量的相关操作

  • 初始化信号量

#include <semaphore.h>
int sem_init(sem_t *sem,int pshared,unsigned int value);
sem:信号量
//pshared:0表示线程间共享,非零表示进程间共享
//value:信号量初始值

  • 摧毁信号量

int sem_destroy(sem_t *sem);

  • 等待信号量

int sem_wait(sem_t *sem);
功能:等到信号量时会将信号量的值减一,对应P操作

  • 发布信号量

int sem_post(sem_t *sem);
功能:资源使用结束后,归还资源将信号量的值加一,对应V操作

3.基于环形队列实现的生产消费模型的原理

上节我们基于阻塞队列实现的生成消费模型中,其空间大小可以进行动态分配。
前面分析得,如果要提高内存分配的性能,我们需要使用固定大小的环形队列来实现这个模型。

生产者每向队列中生产一批数据,消费者就可以从队列中读取到一批数据,所以此时循环队列就变成了交易场所,p表示生产者,c表示消费者,当生产者向队列中push后消费者就可以开始把数据拿走。

这里有几个原则需要遵循

  1. 消费行为不能先于生产行为
  2. 生产行为不能超过消费行为一圈
  3. 虽然空 / 满的状态不好确定,但它俩一定是在同一点
  4. 队列为空时只能有生产行为,为满时只能有消费行为


现在我们模拟实现多线程间的同步过程有下面几个点需要关注:

  • 生产者(P)的操作:取空位增数据

P(sem_space)空位个数- -
V(sem_data)数据个数+ +

  • 消费者(C)的操作:取数据增空位

P ( sem_data ) 数据个数- -
V(sem_space) 空位个数+ +

  • 元素存储

由于环形缓冲区本身就是要降低存储空间分配的开销,因此缓冲区中元素的类型要选好。尽量存储值类型的数据,而不要存储指针(引用)类型的数据。因为指针类型的数据又会引起存储空间(比如堆内存)的分配和释放,使得环形缓冲区的效果打折扣。

  • 判断空&满

环形结构的其实状态和结束状态都是一样的,不好判断为空还是未满,所以我们通过加计数器或者标志位来判断,也可以通过预留一个空的位置作为满的状态。
不过我们现在有信号量这个计数器,所以我们用数组moni 环形队列,用模运算模拟环形的特性。

4. 代码实现

如上内容都理解后,我们就可以实现该模型了。话不多说,看下面的代码:

#include  <iostream>
#include <vector>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>#define NUM 16
using namespace std;class RingQueue{private:vector<int> q;  //模拟环形对列数组int cap;  //资源的数量sem_t data_sem;  //数据sem_t space_sem;  //空位int consume_step;  //消费者所在位置int product_step;  //生产者所在位置
public:RingQueue(int _cap=NUM):q(_cap),cap(_cap){sem_init(&data_sem,0,0);sem_init(&space_sem,0,cap);consume_step=0;product_step=0;}void PutData(const int &data)  //生产者行为{sem_wait(&space_sem);  //等待信号对应减一q[product_step]=data;product_step++;product_step%=cap;  //保证不越界sem_post(&data_sem);  //发布信号对应加一}void GetData(int &data)   //消费者行为{sem_wait(&data_sem);  //等待数据data=q[consume_step];consume_step++;consume_step%=cap;sem_post(&space_sem);  //发布空位}~RingQueue(){sem_destroy(&data_sem);sem_destroy(&space_sem);}
};void *consume(void *arg)
{RingQueue *rq=(RingQueue*)arg;int data;while(1){rq->GetData(data);cout<<"Consume data done:"<<data<<endl;sleep(1);}
}
void *product(void *arg)
{RingQueue *rq=(RingQueue*)arg;srand((unsigned long)time(NULL));while(1){int data=rand()%100+1;rq->PutData(data);cout<<"Product data done:"<<data<<endl;//sleep(1);}
}int main()
{RingQueue rq;pthread_t c,p;pthread_create(&c,NULL,consume,(void*)&rq);pthread_create(&p,NULL,product,(void*)&rq);pthread_join(c,NULL);pthread_join(p,NULL);return 0;
}

运行结果:

相信上面的代码是很容易理解的。通过基于阻塞队列和基于信号量的环形队列实现生产者消费者模型,我们发现实现起来并不是很难,重点是读者们需要理解问题解决的办法和理解模型本身的作用和意义。

下面有篇文章写的非常不错,可以进一步学习参考~
生产者/消费者模式

【Linux】生产者消费者模型-基于环形队列实现相关推荐

  1. [Linux]生产者消费者模型(基于BlockQueue的生产者消费者模型 | 基于环形队列的生产者消费者模型 | 信号量 )

    文章目录 生产者消费者模型 函数调用角度理解生产者消费者模型 生活角度理解生产者消费者模型 为什么要使用生产者消费者模型 生产者消费者模型优点 321原则 基于BlockingQueue的生产者消费者 ...

  2. Linux生产者消费者模型

    文章目录 生产者消费者模型 生产者消费者模型的概念 生产者消费者模型的特点 生产者消费者模型优点 基于BlockingQueue的生产者消费者模型 基于阻塞队列的生产者消费者模型 模拟实现基于阻塞队列 ...

  3. 【Linux入门】多线程(线程概念、生产者消费者模型、消息队列、线程池)万字解说

    目录 1️⃣线程概念 什么是线程 线程的优点 线程的缺点 线程异常 线程异常 Linux进程VS线程 2️⃣线程控制 创建线程 获取线程的id 线程终止 等待线程 线程分离 3️⃣线程互斥 进程线程间 ...

  4. c++面试经验 | 锐捷网络嵌入式委培班 (二)Linux 生产者消费者模型

    先看代码 #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<pthread.h ...

  5. 【生产者消费者模型】

    Linux生产者消费者模型 生产者消费者模型 生产者消费者模型的概念 生产者消费者模型的特点 生产者消费者模型优点 基于BlockingQueue的生产者消费者模型 基于阻塞队列的生产者消费者模型 模 ...

  6. 进程 互斥锁、队列与管道、生产者消费者模型

    目录 1.互斥锁 2.队列与管道 3.生产者消费者模型(Queue) 4.生产者消费者模型(JoinableQueue) 1.互斥锁 首先导入Lock模块 实例化一把锁 但是每次生成子进程的时候都会重 ...

  7. 【Linux篇】第十六篇——生产者消费者模型

    生产者消费者模型 生产者消费者模型的概念 生产者消费者模型的特点 生产者消费者模型优点 基于BlockingQueue的生产消费者模型 基于阻塞队列的生产者消费者模型 模拟实现基于阻塞队列的生产消费模 ...

  8. 操作系统 —— 生产者消费者模型

    文章目录 1. 生产者消费者模型的理解 1.1 串行的概念 1.2 并行的概念 1.3 简单总结: 2. 基于阻塞队列(block queue)实现此模型 2.1 阻塞队列的实现 2.2 使用阻塞队列 ...

  9. 生产者消费者模型【新版】

    目录 啥是生产者消费者模型? 生产者消费者模型存在问题??如何进行解决呢?? 生产者消费者模型导致的问题 什么是阻塞队列 生产者消费者模型优点 生产者消费者模型实现 Message MessageQu ...

最新文章

  1. webservice(二)简单实例
  2. javaWeb实现文件上传与下载 (转)
  3. 国产360等浏览器安装完flash升级程序不起作用的解决方案
  4. 【今日互联网大事儿】聚美市值缩水一半
  5. 嵌入式Linux LED,键盘,AD驱动程序开发
  6. C#穿透session隔离———Windows服务启动UI交互程序
  7. html——name与value的使用
  8. react 对象渲染_不要过度使用React.useCallback()
  9. 区块链 Gossip Protocol是什么
  10. 应用之星:H5制作教程-最好用的H5制作工具
  11. 英寸、磅等单位的换算
  12. 达拉斯大学计算机硕士专业排名,美国名校之德克萨斯大学达拉斯分校研究生专业排名榜单 优势专业你选对了...
  13. ps盖印图层在哪里_盖印图层在哪
  14. 11、深圳经济特区数据条例
  15. 《OD学Oozie》20160807Oozie
  16. 台式计算机风扇为什么光散热,台式机箱风扇为什么要使用静音风扇
  17. 51单片机之温度传感器与液晶显示屏
  18. 李宏毅深度学习自用笔记(未完)
  19. 2020 GMCPC粤澳赛 心得反思
  20. 透镜成像规律(转自百度百科)

热门文章

  1. 从赋能看,创业就是一场与“不确定性”的战斗
  2. 水轮发电机组计算机监控系统,计算机监控系统在低压水轮发电机组的应用
  3. 2.2 数据通信的基础知识
  4. PowerPoint2007通过版式创建幻灯片
  5. 利用cv2.rectangle()绘制矩形框(python)
  6. Android设备开机时间统计
  7. 上班无聊怎么办....
  8. Oracle中declare如何使用
  9. 婚礼拍得像MV一样,好感动
  10. Linux系统用什么翻译软件,Linux安装翻译软件StarDict的步骤(自己的操作总结)