定义

生产者消费者问题(英语: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;}
}

生产者和消费者问题详解相关推荐

  1. 生产者/消费者模型详解(基于Java)

    title: 生产者消费者模型 tags: 多线程 synchronized 锁 wait() notify() 生产者/消费者模型原理以及代码实现 一.生产者/消费者模型原理 所谓的生产者消费者模型 ...

  2. 单线程下的生产者--消费者模式详解,wait和sleep的区别

    1. 单线程下的生产者--消费者模式 1.1 该模式下,一个线程生产数据,另一个线程处理数据.当数据还没被处理,那么生产数据的线程进入等待状态:如果数据还没生产,那么处理数据的线程进入等待状态,代码及 ...

  3. java中的生产者消费者模式详解

    方式 一: Synchronized方式 注:此种方式会造成资源的浪费: 利用锁的notifyAll()方法会将所有的线程都唤醒,会造成资源的浪费 class Resource {private St ...

  4. C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)

    前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题--生产者消费者模型,并给出 ...

  5. 操作系统实验 生产者消费者问题详解

    操作系统课程设计 生产者消费者实验报告 一.实验目的 加深对进程概念的理解,明确进程与程序的区别. 认识并发执行的本质. 理解和掌握Linux和Windows进程通信系统调用的功能,通过实验和学习,提 ...

  6. Dubbo host配置映射内网IP导致消费者无法连接到生产者提供的服务详解

    环境:Cent OS 7.0 背景: 这篇文章的标题可以设置为如下几个,因为他都是同一个解决方法: (1)Dubbo注册zookepper时为什么会自动使用内网IP? (2)Dubbo消费者无法连接到 ...

  7. 生产者消费者模型详解

    生产者消费者模型 文章目录 生产者消费者模型 什么是生产者消费者模型 基于BlockingQueue的生产者消费者模型 单生产者单消费者模型 多生产者多消费者模型 什么是生产者消费者模型 生产者消费者 ...

  8. Java线程生产者消费者问题详解

    问题描述         生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例.生产 ...

  9. 生产者消费者模型详解以及实现

    生产者消费者模式 我们先来看看什么是生产者消费者模式,生产者消费者模式是程序设计中非常常见的一种设计模式,被广泛运用在解耦.消息队列等场景.在现实世界中,我们把生产商品的一方称为生产者,把消费商品的一 ...

最新文章

  1. win2003 配置跨域邮件服务器
  2. 数据仓库中宽表的设计原则_实际项目中交互设计原则的运用
  3. Leetcode861翻转矩阵后的得分(C++题解):贪心
  4. openssl创建CA、申请证书及其给web服务颁发证书
  5. knockout + easyui = koeasyui
  6. JAVA中的内部类与C++内部类的区别
  7. 一个没有担当的领导,通常会有哪些表现?
  8. GRUB 和 GRUB2 的区别
  9. .NET简谈反射(动态调用)
  10. storm风暴英雄 tempo_Tempostorm战队攻略:新版本下的终结者天赋
  11. HTML5期末大作业:美食坊网站设计——美食坊美食购物主题(15页) HTML+CSS+JavaScript
  12. 学习微服务最好的方式:阅读《微服务架构设计模式》
  13. 【软件测试】——软件测试经验总结
  14. rar压缩包找回压缩密码
  15. 在Win32API窗体下实现透明背景
  16. Java定时任务处理异常空指针,执行定时器报空指针异常
  17. PDPS软件:3D空间扫描功能介绍与使用方法
  18. 使用PDF编辑器为PDF文档添加页码教程
  19. 如何将html文件发送邮件,怎么给邮件发送HTML
  20. 西电杨宗凯调研计算机学院,杨宗凯调研指导研究生工作:深化研究生教育改革...

热门文章

  1. 利用hexo和github搭建静态博客(一)
  2. matlab两轮自平衡小车,(2-3合刊) 基于MEMS惯性传感器的两轮自平衡小车设计
  3. JavaCard开发环境搭建
  4. BUUCTF [HITCON 2016] Leaking
  5. FPGA实验记录二:VHDL组合逻辑-时序逻辑练习
  6. 蝶衣王究竟怎么样?来和大家聊一聊
  7. 电化学传感器电路设计
  8. coc跑团san数值规则_从新手到入门但其实没有入门的coc跑团教程---pl篇
  9. php转行当保安,一个保安转行做头条号年入50万,他是怎么做到的?
  10. “永不放弃”成就了再结晶宝石