CHS_09.2.3.6_2+多生产者-多消费者

CHS_09.2.3.6_2+多生产者-多消费者

  • 问题描述
    • 问题分析
    • 如何实现
    • 如何实现
    • 假如我们把盘子的容量设为二
    • 知识回顾

在这个小节中 我们会学习一个多生产者 多消费者的这样一个问题模型

问题描述

在这里插入图片描述

先来看一下问题的描述 假设桌子上面有一个盘子 每次只能向这个盘子里放一个水果

有四个人 父亲 母亲 女儿和儿子 父亲每一次会向盘子里放一个苹果

而女儿专门等着吃盘子里面的苹果 所以如果盘子里有苹果的话 女儿会把

这个苹果给取出并且把它吃掉 另外母亲会专门往盘子里面放橘子

儿子又专门等着母亲把橘子放到盘子里 之后他会把盘子里的橘子给取出并且吃掉

如果说由于这个盘子的容量是只能装一个水果 所以如果父亲把苹果装到了盘子里的话

那么 如果这个苹果还暂时没有被女儿取出 这个时候母亲又包了一个

橘子 并且他尝试把橘子放到这个盘子里 但由于这个盘子里只能装一个水果 所以 这种

这个时候 母亲把橘子放入盘子 这个行为应该被阻止 另外呢 由于儿子只吃橘子 所以此时如果盘子里装的是一个苹果的话 那么如果这个儿子
正在尝试从盘子里取出水果 那这个水果由于不是他想吃的 所以它的这种行为也应该被阻止或者说阻塞

那这个问题 其实我们可以把它抽象为咱们上一个小节学过的生产者 消费者模型 我们可以把

问题分析

在这里插入图片描述

这个盘子看作是一个大小为一初始为空的缓冲区 然后把父亲和母亲看作是两个生产者进程

把女儿和儿子分别看作是两个消费者进程 当然 与上一节所讲的生产者消费者模型不同的是

这个这个小节中的这个例子 这些生产者和这些消费者 他们所生产的东西和他们所消费的东西类型是不一样的

而上个小节我们介绍的例子当中 所有的生产者生产的都是一种东西 而所有的消费者也都是消费同一种东西 所以这就是为什么把这个小节的这个问题称作多生产者多消费者的

原因 所谓的多 不是指多个 应该把它理解为是多类 不同类别的生产者和不同类别的消费者 他们所需要生产的和所需要消费的
这些产品是不一样的
那么这个问题我们怎么用pv操作来解决呢

我们用上个小节学习过的方法来一步一步一次分析 首先我们来看一下这个题目当中所描述的场景
总共有几类进程
很显然 父亲母亲 女儿儿子 他们各属于一类进程

另外呢 他们之间是否存在同步和互斥的关系 之前我们说过

这个题目当中这个盘子 我们可以把它看作是一个缓冲区 而上一节我们聊过

对于缓冲区的访问 一般来说都需要互斥的进行 所以我们需要实现对盘子这种缓冲区的互斥访问

另外 是否存在这种一前一后的同步关系呢 首先

父亲要将苹果放入盘子之后 女儿才能取到苹果 所以父亲进程和女儿进程他们之间有一对同步关系

另外 母亲要把橘子放入盘子之后 儿子才可以取到橘子 所以他们俩之间也存在一对同步关系

第三只有盘子为空的时候 父亲或母亲才可以从 才可以把水果放到盘子当中

而这个地方的盘子为空 这个事件其实既可以由儿子触发 也可以由女儿触发 假如说盘子里放的是橘子 那么盘子为空 这个事件就应该由

儿子来触发 由儿子取走橘子 而如果说盘子里放的是苹果的话 那就应该由女儿取走苹果 然后由女儿来触发 盘子为空这个事件

只有盘子为空这个事件发生之后 才能允许父亲进程或母亲进程往盘子里放入水果

所以女儿和儿子 他们可能会触发一个事件来引发父亲或者母亲

往盘子里 放入水果 那这就是这个题目当中所包含的互斥关系和同步关系

第二步 我们来看一下各个进程之间的pv操作流程大概是什么样的

我们知道实现互斥其实很简单 无非就是在访问这个互斥临界资源的
之前和访问临界资源之后 分别对互斥变量实行一个p操作和一个v操作

而实现同步关系 其实我们之前也说过 我们只需要遵循一个原则 就是所谓的前V后P

也就是前面的这个事件发生了之后 我们需要执行一个V操作

后面的这个事件发生之前 我们需要执行一个p操作 那就是这个样子

那么 对于实现互斥关系来说 我们当然是需要设置一个

初值为一的这种互斥信号量 而对于这些同步关系来说 我们需要根据具体的情况来判断每一个同步变量应该设为多少

父亲进程需要把苹果放入盘子 女儿才能取到苹果 所以我们需要设置一个同步信号量叫做apple用来实现这个同步关系

而由于刚开始盘子里面是没有苹果的 所以我们把这个值设为初始值 设为零

只有父亲对这个同步信号量执行v操作 之后 女儿对这个信同步信号量执行的p操作才不会被阻塞 那同样的 对母亲进程来说 我们也设置一个叫做orange 也就是橘子的一个同步信号量 他的初始值也设为一因为刚开始盘子里面是没有橘

另外呢 只有盘子为空的时候 父亲和母亲才可以放入水果 而刚开始

盘子本来就是空的 所以父亲和母亲在刚开始其实就可以向盘子里放入一个

水果 所以这个地方像这一对同步关系 我们就需要为它设置一个初值为一的同步信号量 用来表示此时盘子是否为空 那么来看一下具体的代码实现 我们总共申明了四个

如何实现

在这里插入图片描述

信号量一个 其中一个是互斥信号量 另外三个是同步信号量 父亲进程和母亲进程做的事情就是不断的准备一个自己的水果

然后再把自己的水果放到盘子里 而女儿进程和儿子进程做的事情就是不断的从盘子中取出自己喜欢的水果 并且把这个水果给吃掉

父亲进程在放入水果之前需要先检查这个盘子是否为空

所以 在苹果放入盘子之前 父亲进程需要对这个信号量进行一个p操作来检查此时盘子中到底还可以放多少个水果

那如果这个苹果已经放入盘子之后父亲进程又需要对

apple这个同步信号量执行一个v操作 用来让apple的值加一

来告诉女儿进程 此时苹果盘子中的苹果数量已经加一了

母亲进程也一样 他在把橘子放入盘子之前需要同样需要对盘子中还可以放多少个水果进行检查

如果说此时这个盘子中已经有别的水果 那么母亲进程会被阻塞 在这个地方会被阻塞

而当母亲进程把橘子放入盘子之后 他同样也需要对orange这个同步信号量执行一个v操作来通知儿子进程 此时盘子当中的橘子数已经加一了

那么女儿进程和儿子进程在取出自己喜欢的水果之前 分别需要检查此时这个盘子当中是否已经有自己喜欢的水果 所以女儿进程是对apple这个信号量执行p操作

儿子进程是对orange这个信号量执行p操作 而女儿进程在从盘子中取出苹果之前需要先检查此时苹果的是盘子中苹果的数量是否足够 如果没有苹果的话 它将被阻塞

而当他把苹果取出之后 又需要对plate这个信号量执行v操作 用来告诉父亲进程和母亲进程 此时盘子已经变空了

那么儿子进程也和女儿进程也类似 只不过是他在检查的时候 是检查盘子当中是否有橘子

那这样的话 我们就实现了咱们在这个图中表示的这些同步关系 另外 我们还需要实现各个进程对盘子这种缓冲区的互斥访问

所以我们在这些进程访问盘子之前 对这个互斥信号量执行一个p操作 访问之后又执行一个V操作 分别是对这个临界区进行加锁和解锁

其他的这些进程也一样 那接下来我们再来考虑一个问题 可不可以不要这个互斥信号量呢

如何实现

在这里插入图片描述

就是这样子 我们把互斥信号量去掉 并且把对互斥信号量的pv操作也都去掉

那我们来分析一下 如果是这样的话 这些进程会怎么并发执行 刚开始

由于apple和orange这两个信号量的数量都为零所以女儿进程和儿子进程无论谁上处理机运行 肯定在执行到这个p操作的时候都会被阻塞

那么 我们假设刚开始是由父亲进程上处理机运行 那么他首先会对盘子这个同步信号量执行一个p操作 由于刚开始这个信号量的值位一也就是说盘子这种资源还足够 所以父亲进程 他可以顺利的跳过这个p操作 然后开始

把苹果放入盘子当中 而这个时候 如果说切换回母亲进程 那么当母亲进程对盘子的信号量执行p操作的时候 由于这个值已经变为了零
盘子这种资源已经不够了 所以母亲进程 在这个时候会被阻塞等待盘子

而当父亲进程把苹果放入盘子之后 他又会对apple这个同步信号量执行一个V操作
这个时候 女儿进程她又会被唤醒
然后从盘子当中取出苹果 之后 女儿进程又会对plate这个同步信号量执行一个V操作

由于之前母亲进程是因为这个信号量而被阻塞的 所以当他执行v操作之后 母亲进程就会被唤醒

之后母亲进程就可以开始顺利的访问这个临界区资源 而当母亲进程在访问盘子这种临界资源的时候

由于plate的值为零然后orange和apple的值也都为零所以除了母亲进程以外 别的这些进程即使上处理机运行 也肯定会被卡在这个p操作 这也就会被阻塞

所以通过刚才的分析 我们会发现 在这个题目当中 我们即使不设置专门的互斥信号量没有mutex

我们依然可以实现这些进程对盘子这种临界区的互斥访问

为什么呢 其实原因在于这个题目当中的这个缓冲区的大小只为一

大家可以自己再尝试分析一下更多的情况 apple orange和plate

这三个同步信号量同一时刻最多只有一个 会是一而这几个进程刚开始都需要对其中的某一个信号量执行p操作 由于这三个同步信号量当中同一时刻最多只有一个的值会是一所以这些进程执行各自

的p操作的时候 最多只有一个进程不会被阻塞 可以顺利的进入这个临界区进行访问

假如我们把盘子的容量设为二也就是这个缓冲区的容量把它设为二的话 会发生什么情况呢

假如我们把盘子的容量设为二

在这里插入图片描述
在这里插入图片描述

假设刚开始盘子就可以放两个水果 那么刚开始父亲进程执行p操作 发现盘子资源足够 所以他可以进入临界区开始访问盘子

母亲进程在执行p操作之后也发现盘子这种资源依然是足够的 所以他同样也会进入这个
临界区对盘子这种临界资源进行访问
所以这就发生了父亲进程和母亲进程两个进程同时访问盘子这种临界资源的情况

那通过上个小节的讲解 我们知道 如果两个生产者进程 他们同时对一个缓冲区进行访问的话 那么有可能会导致数据覆盖的问题

这个地方也一样 因此如果我们在生产者 消费者问题当中遇到了缓冲区大于一的情况 那么我们就必须设置一个互斥信号量
mutex来保证各个进程是可以互斥的访问缓冲区的
而如果缓冲区大小等于一的话 那么我们即使不设置这个互斥信号量 有可能也可以实现互斥访问临界区这个事情 当然这不是绝对的 只是有可能 不

需要设置互斥信号量要具体问题具体分析 如果大家在考试的时候遇到缓冲区大小为一的情况的时候 那么

可以自己分析一下 如果能确定不需要使用互斥信号量的话 那么不设置也可以 但如果来不及仔细分析的话 大家最好是加上这个互斥信号量 因为加上了肯定也没错 不过我们需要注意的是上个小节强调过的那个问题实现互斥的

对于mutex 那个信号量的p操作一定要在实现同步的p操作之后 否则是有可能会引起死锁的

知识回顾

在这里插入图片描述
在这里插入图片描述

这个点大家还能不能回忆起来呢 那么和之前的小节一样 大家也需要体会p v操作这种相关的题目的解题思路

和上个小节介绍的经典的生产者消费者问题不太一样的是 这个小节这个模型是多生产者多消费者

他的同步关系要比之前那个小节所介绍的同步关系要复杂的多 大家需要注意一个事情

在分析这种同步问题的时候 我们不能从单个进程的行为的角度来触发 而需要把这种一前一后的事情
把它看作是两个事件的前后关系
这句话我们不太容易理解 我们直接来看例子

如果说在这个题目当中 我们是从单个进程行为的角度来考虑的话

那么 我们会有这样的结论 首先 从题目当中我们可以知道 如果盘子里边装的是苹果

那么一定要女儿取走苹果之后 母父亲和母亲才可以放入水果

所以 当女儿进程取走苹果之后 可能会导致父亲进程

可以放入水果 同样也可能导致母亲进程 可以放入水果 另外

如果盘子里装的是橘子话 那么儿子取走橘子之后 可能会导致父亲进程可以放入水果 也可能会导致母亲进程可以放入水果

那么如果我们从这个 这种单个进程的行为来分析的话 那仅仅这两句话 我们就可以分析出这样的四个同步关系

那么有四个同步关系是不是就意味着我们要设置四个同步信号量来分别实现这四个一前一后的这种关系了呢

当然不是其实正确的分析方法 我们应该从事件的角度来考虑

我们应该把刚才所描述的这种进程行为的前后关系 也就是 女儿取走

取走水果这个行为导致父亲可以放入水果 同样也导致母亲可以放入水果 从这种行进程行为的前后关系 我们可以把它抽象为一对事件的

前后关系 我们应该把进程同步问题 把它理解为 某一个事件 一定要求发生在另一个事件之前

而不是某一个进程的行为要发生在另一个进程的行为之前 那么我们如果从事件的角度来考虑的话

刚才所描述的这两句话 其实我们可以抽象为两个事件 盘子变空的事件可一定要发生在放入水果这个事件之前

而盘子变空这个事件 既可以由儿子进程来引发 也可以由

女儿进程来引发放水果的这个事件 既可以是父亲进程来执行也可以是母亲进程来执行

所以刚才我们看起来有四对同步关系 其实我们如果把从事件的角度来考虑的话 我们可以把它抽象为一对事件的前后关系

而这些事件可以由哪个进程来引发 那么这个进程就需要对这个事件对应的

同步信号量 执行一个v操作 同样的父亲进程和母亲进程都有可能会执行放入水果这个事件 所以在放他们执行各自的放入水果事件之前

我们需要对这个事件对应的同步信号量执行 分别执行一个p操作

那这个地方大家还需要自己认真的体会 这对于初学者来说确实比较容易犯这样的问题

那如果能把这个部分的内容理解了 以后再做同步相关的这些大题的时候 应该问题就不大了

好的 那么这就是这个小节的全部内容

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/452311.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matomo 访问图形显示异常

近期我们的把 PHP 系统完全升级后,访问 Matomo 的站点有关访问的曲线无法显示。 出现的情况如下图: 我们可以看到图片中有关的访问曲线无法显示。 如果具体直接访问链接的话,会有下面的错误信息。 问题和解决 出现上面问题的原因是缺少 ph…

vue3-插件

插件 插件 (Plugins) 是一种能为 Vue 添加全局功能的工具代码。 安装一个插件 在 main.ts 或者 main.js 中的入口文件内 import { createApp } from vueconst app createApp({})app.use(myPlugin, {/* 可选的选项 */ })一个插件可以是一个拥有 install() 方法的对象&#…

第六讲:文件操作

第六讲:文件操作 文件夹创建文件夹移动文件夹复制文件夹删除文件夹文件操作文件读取文件写入文件文件夹 创建文件夹 定义创建文件夹函数:chmk_path()定义一个函数 chmk_path(),这个函数的功能是创建文件夹。 首先需要导入操作系统接口模块——os 模块,这个模块中包含某些函…

面向切面编程的魔法 ---【Spring AOP】

目录 什么是AOP? 什么是Spring AOP? Spring AOP 的实现 1. 引入依赖 2. 实现切面逻辑 概念理解 在软件开发中,面向切面编程(AOP)是一种强大的编程范式,它允许我们在应用程序的不同模块中提取横切关注点…

微信网页授权之使用完整服务解决方案

目录 微信网页授权能力调整造成的问题 能力调整的内容和理由 原有运行方案 is_snapshotuser字段 改造原有方案 如何复现测试场景 小结 微信网页授权能力调整造成的问题 依附于第三方的开发,做为开发者经常会遇到第三方进行规范和开发的调整,如开…

线程池,定时器以及阻塞队列(生产者/消费者模型)

💓 博客主页:从零开始的-CodeNinja之路 ⏩ 收录专栏:线程池,定时器以及阻塞队列(生产者/消费者模型) 🎉欢迎大家点赞👍评论📝收藏⭐文章 实现线程池,定时器以及阻塞队列,生产者/消费者模型 线程池线程池…

Linux------进程优先级与进程切换

目录 一、进程优先级 二、优先级与权限的区别 三、优先级的查看 四、进程优先级修改 五、进程切换 六、linux2.6内核调度队列与调度原理 一、进程优先级 首先我们得知道一个进程总是需要排队的,他一会在运行队列中排队等待运行,一会在设备的等待队…

【AcWing第141场周赛】AcWing 5464. 客人数量(A题)

文章目录 一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、时间复杂度3、代码详解 一、题目 1、原题链接 5464. 客人数量 2、题目描述 二、解题报告 1、思路分析 按题意模拟,累加求和即可,最终输出结果。 2、时间复杂度 时间复杂度为O(n…

git命令远程仓库推送本地项目报错了,解决方案

如果你在使用git命令上传本地项目到远程仓库遇到了如下错误: Updates were rejected because the tip of your current branch is behind。n 别慌,肯定是你的远程仓库里面有原始文件,需要你提前进行一下合并操作,然后才能使用pu…

ES高可用架构涉及常用功能整理

ES高可用架构涉及常用功能整理 1. es的高可用系统架构和相关组件2. es的核心参数2.1 常规配置2.2 特殊优化配置2.2.1 数据分片按ip打散2.2.2 数据分片机架感知2.2.3 强制要求数据分片机架感知2.2.4 写入线程池优化2.2.5 分片balance优化2.2.6 限流控制器优化 3. es常用命令3.1 …

用Python Tkinter打造的精彩连连看小游戏【附源码】

文章目录 连连看小游戏:用Python Tkinter打造的精彩游戏体验游戏简介技术背景MainWindow类:职责:方法:Point类: 主执行部分:完整代码:总结: 连连看小游戏:用Python Tkinter打造的精彩游戏体验 在丰富多彩的游戏世界中&#xff0c…