生产者和消费者问题详解
定义
生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
思路
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法[1]等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。
java实现
使用信号灯的算法
信号灯可以避免上述唤醒指令不起作用的情况。该方法(见下面的代码)使用了两个信号灯,fillCount 和 emptyCount。fillCount 用于记录缓冲区中将被读取的数据项数(实际上就是有多少数据项在缓冲区里),emptyCount 用于记录缓冲区中空闲空间数。当有新数据项被放入缓冲区时,fillCount 增加,emptyCount 减少。如果在生产者尝试减少 emptyCount 的时候发现其值为零,那么生产者就进入休眠。等到有数据项被消耗,emptyCount 增加的时候,生产者才被唤醒。消费者的行为类似。
semaphore fillCount = 0; // 生产的项目
semaphore emptyCount = BUFFER_SIZE; // 剩余空间procedure producer() {while (true) {item = produceItem();down(emptyCount);putItemIntoBuffer(item);up(fillCount);}
}procedure consumer() {while (true) {down(fillCount);item = removeItemFromBuffer();up(emptyCount);consumeItem(item);}
}
上述方法在只有一个生产者和一个消费者时能解决问题。对于多个生产者或者多个消费者共享缓冲区的情况,该算法也会导致竞争条件,出现两个或以上的线程同时读或写同一个缓冲区槽的情况。为了说明这种情况是如何发生的,可以假设 putItemIntoBuffer() 的一种可能的实现:先寻找下一个可用空槽,然后写入数据项。下列情形是可能出现的:
两个生产者都减少 emptyCount 的值;
某一生产者寻找到下一个可用空槽;
另一生产者也找到了下一个可用空槽,结果和上一步被找到的是同一个空槽;
两个生产者向可用空槽写入数据。
多个生产者和消费者的问题
为了解决这个问题,需要在保证同一时刻只有一个生产者能够执行 putItemIntoBuffer()。也就是说,需要寻找一种方法来互斥地执行临界区的代码。为了达到这个目的,可引入一个二值信号灯 mutex,其值只能为 1 或者 0。如果把线程放入 down(mutex) 和 up(mutex) 之间,就可以限制只有一个线程能被执行。多生产者、消费者的解决算法如下:
semaphore mutex = 1;
semaphore fillCount = 0;
semaphore emptyCount = BUFFER_SIZE;procedure producer() {while (true) {item = produceItem();down(emptyCount);down(mutex);putItemIntoBuffer(item);up(mutex);up(fillCount);}
}
procedure consumer() {while (true) {down(fillCount);down(mutex);item = removeItemFromBuffer();up(mutex);up(emptyCount);consumeItem(item);}
}
c语言的经典解法
问题分析
1) 关系分析。生产者和消费者对缓冲区互斥访问是互斥关系,同时生产者和消费者又是一个相互协作的关系,只有生产者生产之后,消费者才能消费,他们也是同步关系。
2) 整理思路。这里比较简单,只有生产者和消费者两个进程,正好是这两个进程存在着互斥关系和同步关系。那么需要解决的是互斥和同步PV操作的位置。
3) 信号量设置。信号量mutex作为互斥信号量,它用于控制互斥访问缓冲池,互斥信号量初值为1;信号量full用于记录当前缓冲池中“满”缓冲区数,初值为0。信号量empty 用于记录当前缓冲池中“空”缓冲区数,初值为n。
生产者-消费者进程的描述如下:
semaphore mutex=1; //临界区互斥信号量
semaphore empty=n; //空闲缓冲区
semaphore full=0; //缓冲区初始化为空
producer () { //生产者进程while(1){produce an item in nextp; //生产数据P(empty); //获取空缓冲区单元P(mutex); //进入临界区.add nextp to buffer; //将数据放入缓冲区V(mutex); //离开临界区,释放互斥信号量V(full); //满缓冲区数加1}
}
consumer () { //消费者进程while(1){P(full); //获取满缓冲区单元P(mutex); // 进入临界区remove an item from buffer; //从缓冲区中取出数据V (mutex); //离开临界区,释放互斥信号量V (empty) ; //空缓冲区数加1consume the item; //消费数据}
}
复杂的生产者和消费者问题
问题描述
桌子上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。只有盘子为空时,爸爸或妈妈就可向盘子中放一个水果;仅当盘子中有自己需要的水果时,儿子或女儿
可以从盘子中取出。
问题分析
1) 关系分析。这里的关系稍复杂一些,首先由每次只能向其中放入一只水果可知爸爸和妈妈是互斥关系。爸爸和女儿、妈妈和儿子是同步关系,而且这两对进程必须连起来,儿子和女儿之间没有互斥和同步关系,因为他们是选择条件执行,不可能并发,如图2-8所示。
2) 整理思路。这里有4个进程,实际上可以抽象为两个生产者和两个消费者被连接到大小为1的缓冲区上。
3) 信号量设置。首先设置信号量plate为互斥信号量,表示是否允许向盘子放入水果,初值为1,表示允许放入,且只允许放入一个。信号量 apple表示盘子中是否有苹果,初值为0,表示盘子为空,不许取,若apple=l可以取。信号量orange表示盘子中是否有橘子,初值为0,表示盘子为空,不许取,若orange=l可以取。解决该问题的代码如下:
semaphore plate=l, apple=0, orange=0;
dad() { //父亲进程while (1) {prepare an apple;P(plate) ; //互斥向盘中取、放水果put the apple on the plate; //向盘中放苹果V(apple); //允许取苹果}
}
mom() { // 母亲进程while(1) {prepare an orange;P(plate); //互斥向盘中取、放水果put the orange on the plate; //向盘中放橘子V(orange); //允许取橘子}
}
son(){ //儿子进程while(1){P(orange) ; //互斥向盘中取橘子take an orange from the plate;V(plate); //允许向盘中取、放水果eat the orange;}
}
daughter () { //女儿进程while(1) {P(apple); // 互斥向盘中取苹果take an apple from the plate;V(plate); //运行向盘中取、放水果eat the apple;}
}
生产者和消费者问题详解相关推荐
- 生产者/消费者模型详解(基于Java)
title: 生产者消费者模型 tags: 多线程 synchronized 锁 wait() notify() 生产者/消费者模型原理以及代码实现 一.生产者/消费者模型原理 所谓的生产者消费者模型 ...
- 单线程下的生产者--消费者模式详解,wait和sleep的区别
1. 单线程下的生产者--消费者模式 1.1 该模式下,一个线程生产数据,另一个线程处理数据.当数据还没被处理,那么生产数据的线程进入等待状态:如果数据还没生产,那么处理数据的线程进入等待状态,代码及 ...
- java中的生产者消费者模式详解
方式 一: Synchronized方式 注:此种方式会造成资源的浪费: 利用锁的notifyAll()方法会将所有的线程都唤醒,会造成资源的浪费 class Resource {private St ...
- C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)
前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题--生产者消费者模型,并给出 ...
- 操作系统实验 生产者消费者问题详解
操作系统课程设计 生产者消费者实验报告 一.实验目的 加深对进程概念的理解,明确进程与程序的区别. 认识并发执行的本质. 理解和掌握Linux和Windows进程通信系统调用的功能,通过实验和学习,提 ...
- Dubbo host配置映射内网IP导致消费者无法连接到生产者提供的服务详解
环境:Cent OS 7.0 背景: 这篇文章的标题可以设置为如下几个,因为他都是同一个解决方法: (1)Dubbo注册zookepper时为什么会自动使用内网IP? (2)Dubbo消费者无法连接到 ...
- 生产者消费者模型详解
生产者消费者模型 文章目录 生产者消费者模型 什么是生产者消费者模型 基于BlockingQueue的生产者消费者模型 单生产者单消费者模型 多生产者多消费者模型 什么是生产者消费者模型 生产者消费者 ...
- Java线程生产者消费者问题详解
问题描述 生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例.生产 ...
- 生产者消费者模型详解以及实现
生产者消费者模式 我们先来看看什么是生产者消费者模式,生产者消费者模式是程序设计中非常常见的一种设计模式,被广泛运用在解耦.消息队列等场景.在现实世界中,我们把生产商品的一方称为生产者,把消费商品的一 ...
最新文章
- win2003 配置跨域邮件服务器
- 数据仓库中宽表的设计原则_实际项目中交互设计原则的运用
- Leetcode861翻转矩阵后的得分(C++题解):贪心
- openssl创建CA、申请证书及其给web服务颁发证书
- knockout + easyui = koeasyui
- JAVA中的内部类与C++内部类的区别
- 一个没有担当的领导,通常会有哪些表现?
- GRUB 和 GRUB2 的区别
- .NET简谈反射(动态调用)
- storm风暴英雄 tempo_Tempostorm战队攻略:新版本下的终结者天赋
- HTML5期末大作业:美食坊网站设计——美食坊美食购物主题(15页) HTML+CSS+JavaScript
- 学习微服务最好的方式:阅读《微服务架构设计模式》
- 【软件测试】——软件测试经验总结
- rar压缩包找回压缩密码
- 在Win32API窗体下实现透明背景
- Java定时任务处理异常空指针,执行定时器报空指针异常
- PDPS软件:3D空间扫描功能介绍与使用方法
- 使用PDF编辑器为PDF文档添加页码教程
- 如何将html文件发送邮件,怎么给邮件发送HTML
- 西电杨宗凯调研计算机学院,杨宗凯调研指导研究生工作:深化研究生教育改革...
热门文章
- 利用hexo和github搭建静态博客(一)
- matlab两轮自平衡小车,(2-3合刊) 基于MEMS惯性传感器的两轮自平衡小车设计
- JavaCard开发环境搭建
- BUUCTF [HITCON 2016] Leaking
- FPGA实验记录二:VHDL组合逻辑-时序逻辑练习
- 蝶衣王究竟怎么样?来和大家聊一聊
- 电化学传感器电路设计
- coc跑团san数值规则_从新手到入门但其实没有入门的coc跑团教程---pl篇
- php转行当保安,一个保安转行做头条号年入50万,他是怎么做到的?
- “永不放弃”成就了再结晶宝石