生产者和消费者模型介绍
一、概念引入
日常生活中,每当我们缺少某些生活用品时,我们都会去超市进行购买,那么,你有没有想过,你是以什么身份去的超市呢?相信大部分人都会说自己是消费者,确实如此,那么既然我们是消费者,又是谁替我们生产各种各样的商品呢?当然是超市的各大供货商,自然而然地也就成了我们的生产者。如此一来,生产者有了,消费者也有了,那么将二者联系起来的超市又该作何理解呢?诚然,它本身是作为一座交易场所而诞生。
将上述场景例比到我们实际的软件开发过程中,经常会见到这样一幕:代码的某个模块负责生产数据(供货商),而生产出来的数据却不得不交给另一模块(消费者)来对其进行处理,在这之间我们必须要有一个类似上述超市的东西来存储数据(超市),这就抽象除了我们的生产者/消费者模型。
其中,产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者;生产者和消费者之间的中介就叫做缓冲区。
三者之间的结构图:
为了方便理解,再列举一个寄信的例子:
1、你把信写好——相当于生产者制造数据
2、你把信放入邮筒——相当于生产者把数据放入缓冲区
3、邮递员把信从邮筒取出——相当于消费者把数据取出缓冲区
4、邮递员把信拿去邮局做相应的处理——相当于消费者处理数据
二、为什么要使用生产者消费者模型
归根结底来说,生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。
在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,所以便有了生产者和消费者模式。
三、生产者/消费者模型的优点
1、解耦,即降低生产者和消费者之间的依赖关系。
例如上述写信的例子,如果不使用邮筒(也就是缓区),你必须得把信直接交给邮递员。有同学会说,直接给邮递员不是挺简单的嘛?其实不简单,你必须得认识谁是邮递员,才能把信给他(光凭身上穿的制服,万一有人假冒,就惨了 )。这就产生和你和邮递员之间的依赖(相当于生产者和消费者的强耦合)。万一哪天邮递员换人了,你还要重新认识一下(相当于消费者变化导致修改生产者代码)。而邮筒相对来说比较固定,你依赖它的成本就比较低(相当于和缓冲区之间的弱耦合)。
2、支持并发,即生产者和消费者可以是两个独立的并发主体,互不干扰的运行。
从寄信的例子来看。如果没有邮筒,你得拿着信傻站在路口等邮递员过来收(相当于生产者阻塞);又或者邮递员得挨家挨户问,谁要寄信(相当于消费者轮询)。不管是哪种方法,效率都比较低。
3、支持忙闲不均,如果制造数据的速度时快时慢,缓冲区可以对其进行适当缓冲。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
为了充分复用,我们再拿寄信的例子来说事。假设邮递员一次只能带走1000封信。万一某次碰上情人节(也可能是圣诞节)送贺卡,需要寄出去的信超过1000封,这时候邮筒这个缓冲区就派上用场了。邮递员把来不及带走的信暂存在邮筒中,等下次过来时再拿走。
四、生产者/消费者模型的记忆原则
为了方便记忆,我对其进行了如下总结:
三二一原则:三种关系、两个角色、一个场所
三种关系:
1生产者与生产者(互斥)
2生产者与消费者(同步与互斥)
3消费者与消费者(互斥)
两个角色:
1生产者
2消费者
一个场所:
1缓冲区
五、生产者/消费者模型的学习旅程
1、确定数据单元
★啥是数据单元
向缓冲区拿放数据的一个基本数据单元。简单地说,每次生产者放到缓冲区的,就是一个数据单元;每次消费者从缓冲区取出的,也是一个数据单元。
★数据单元的特性
◇关联到业务对象:数据单元必须关联到某种业务对象
◇完整性:保证每一个数据单元的完整
◇独立性:各个数据单元之间没有互相依赖
◇颗粒度:业务对象和数据单元之间的对应比例
2、学习队列缓冲区
★线程方式
◇内存分配的性能:内存分配的开销问题—->环形缓冲区
◇同步和互斥的性能:例如信号量、互斥量等的开销—->双缓冲区
◇适用于队列的场合:适用于数据流量不是很大的场合
★进程方式
◇匿名管道:生产者进程在管道的写端放入数据;消费者
进程在管道的读端取出数据。
好处:
1》跨平台发方便。
2》跨语言方便。
3》有利于降低开发、调试成本。
不足:
1》生产者进程和消费者进程必须得在同一台主机上,无法跨机器通讯。
2》只适用于一对一通信。
3》在某些情况下,程序不便于对管道进行操纵(比如调整管道缓冲区尺寸)。
4》只能单向通信。
◇SOCKET(TCP方式):类似IPC方式,同样保证了
数据的顺序到达;同样有缓冲的机制。
优点:
1》可以跨机器(便于实现分布式)。
2》便于将来扩展成为多对一或者一对多。
3》可以设置阻塞和非阻塞方法,用起来比较灵活。
4》支持双向通讯,有利于消费者反馈信息。
不足:
编程复杂。
虽然TCP在很多方面比UDP可靠,但鉴于跨机器通讯先天的不可预料性,可以在生产者进程和消费者进程内部各自再引入基于线程的”生产者/消费者模式”。
3、学习环形缓冲区:
★环形缓冲区与队列缓冲区
◇外部接口相似:它也有一个写入端(用于push)和⼀一个读出端(用于pop),也有缓冲区“满”和“空”的状态。
◇内部结构迥异:
从上图可以看出,环形缓冲区所有的push和pop操作都是在一个固定的存储空间内进行。而队列缓冲区在push的时候,可能会分配存储空间用于存储新元素;在pop时,可能会释放废弃元素的存储空间。
★环形缓冲区的实现
◇数组方式 和链表方式
◇读写操作
◇判断“空”和“满”
★应用场合
◇用于并发线程
◇用于并发进程
4、学习双缓冲区...
此处略去,有兴趣者可自行研究
生产者和消费者模型介绍相关推荐
- 计算机操作系统生产者和消费者模型的简单介绍
同步互斥小口诀 画图理解题目 判断题目类型 分析进程数目 填写进程模板 补充基本代码(伪代码) 补充PV代码 检查调整代码 注意事项 代码是一步一步写出来的,代码是反复调整写出来的 60%是生产者和消 ...
- 并发无锁队列学习(单生产者单消费者模型)
1.引言 本文介绍单生产者单消费者模型的队列.依据写入队列的内容是定长还是变长,分为单生产者单消费者定长队列和单生产者单消费者变长队列两种. 单生产者单消费者模型的队列操作过程是不须要进行加锁的.生产 ...
- python 进程间同步_python之路29 -- 多进程与进程同步(进程锁、信号量、事件)与进程间的通讯(队列和管道、生产者与消费者模型)与进程池...
所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了.至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠 ...
- linux进程间通信:system V 信号量 生产者和消费者模型编程案例
生产者和消费者模型: 有若干个缓冲区,生产者不断向里填数据,消费者不断从中取数据 两者不冲突的前提: 缓冲区有若干个,且是固定大小,生产者和消费者各有若干个 生产者向缓冲区中填数据前需要判断缓冲区是否 ...
- python生产和消费模型_python queue和生产者和消费者模型
queue队列 当必须安全地在多个线程之间交换信息时,队列在线程编程中特别有用. classqueue.Queue(maxsize=0) #先入先出classqueue.LifoQueue(maxsi ...
- Linux系统编程---17(条件变量及其函数,生产者消费者条件变量模型,生产者与消费者模型(线程安全队列),条件变量优点,信号量及其主要函数,信号量与条件变量的区别,)
条件变量 条件变量本身不是锁!但它也可以造成线程阻塞.通常与互斥锁配合使用.给多线程提供一个会合的场所. 主要应用函数: pthread_cond_init 函数 pthread_cond_destr ...
- Linux系统编程40:多线程之基于环形队列的生产者与消费者模型
文章目录 (1)什么是信号量 (2)与信号量相关的操作 (3)基于环形队列的生产者与消费者模型-信号量(单消费者单生产者) (1)什么是信号量 前面的叙述中,我们通过锁保证了每次只有一个线程进入临界区 ...
- Linux系统编程39:多线程之基于阻塞队列生产者与消费者模型
文章目录 (1)生产者与消费者模型概述 (2)生产者与消费者模型优点 (3)基于阻塞队列(blockingqueue)的生产者消费者模型(单消费者单生产者) (4)基于阻塞队列(blockingque ...
- Go语言编程:使用条件变量Cond和channel通道实现多个生产者和消费者模型
如题,使用条件变量Cond和channel通道实现多个生产者和消费者模型.Go语言天生带有C语言的基因,很多东西和C与很像,但是用起来 绝对比C语言方便.今天用Go语言来实现下多消费者和生产者模型.如 ...
最新文章
- gui窗口遮挡算法_基于 C 语言开发的 GUI 框架
- Docker实战:Docker安装部署RabbitMQ
- MFC框架类、文档类、视图类相互访问(及窗口句柄获取)的方法
- 五款常用邮件管理系统评测
- 坯子库安装不上_kodi如何安装中文插件?kodi中文插件安装方法
- 日出时间php,返回给定的日期与地点的日落时间/ 日出时间
- SDRAM容量的计算方法
- ecology9 系统文件常用说明
- 揭秘React同构应用
- 3dmax导入shp面拉伸建模
- [ArcPy百科]第三节: Geometry信息中的空间参考解析
- Android TextToSpeech(tts)语音播报(文字转语音)
- mysql的服务被删了怎么办_三、MySQL服务构成、基本操作、误删所有用户后的恢复方法...
- 什么是证券市场?证券市场有哪些组成
- Android 中怎么重启APP、重启系统
- web前端网页设计期末课程大作业:企业网页主题网站设计——舞蹈培训11页HTML+CSS+JavaScript
- python 折线图变成直线图_python如何画折线图
- java jdbc程序,Java构建JDBC应用程序的操作
- UCOSIII系统内部任务
- kali安装mwget
热门文章
- Brendan Eich谈Javascript的起源
- STM32两轮自平衡小车(学习记录)——MPU6050
- 基于python的Topsis(优劣解距离)算法的实现(附代码及举例说明)
- node.js调试 BY:色拉油啊油
- Linux下vscode无法查看定义?
- 财务机器人RPA推动财务职能转换
- 科普:QUIC协议原理分析
- 判断一句话是否中文或者英文
- 对某班学生成绩排序。从键盘依次输入某班学生的姓名和成绩(一个班级人数最多不超过50人)并保存,然后分别按学生成绩由高到低顺序输出学生姓名和成绩,成绩相同时,则按输入次序排序。
- LCD液晶显示屏工作原理