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

news2024/11/28 20:43:54

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.coloradmin.cn/o/1432582.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot 各种回滚骚操作实战

事务定义 事务,就是一组操作数据库的动作集合。事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其…

Leetcode高频题:198打家劫舍1

题目链接力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 题目描述 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相…

力扣 121. 买卖股票的最佳时机

题目来源:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/ 好久没写代码了,啥啥都忘了 C题解1:贪心算法。(来源代码随想录) 因为股票就买卖一次,那么贪心的想法很自然就是取…

拖拽按钮: 如何区分点击和拖拽事件 (vueuse实现)

问题:使用vueuse的useDraggable去拽按钮时会触发点击事件,即使设置阻止默认事件还是没用。 方案1:判断拖拽时长 判断拖拽时长,如果大于点击的时间秒数,则被认为是拖拽。小于被认为是点击,则触发点击事件…

【01】C++入门

文章目录 Ⅰ 命名空间1. 命名空间域的产生2. 命名空间域的定义3. 命名空间域的使用 Ⅱ 缺省参数1. 缺省的概念2. 缺省的分类3. 声明和定义不能同时存在缺省参数 Ⅲ 函数重载1. 函数重载概念2. 编译器如何实现函数重载 Ⅳ 引用1. 引用的概念2. 引用的特性3. 引用的使用场景4. 引…

【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案

【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案 大家好 我是寸铁👊 总结了一篇Error: only one service expected goctl一键转换生成rpc服务错误解决方案的文章✨ 喜欢的小伙伴可以点点关注 💝 问题背景 今天寸铁在…

Qt之使用Qt内置图标

一效果 二.原理 Qt内置图标封装在QStyle中,共七十多个图标,可以直接拿来用,能应付不少简单程序需求,不用自己去找图标并添加到资源文件了。 下面是内置图标的枚举定义: enum StandardPixmap {SP_TitleBarMenuButton,SP_TitleBarMinButton,SP_TitleBarMaxButton,SP_T…

[Python] scikit-learn中数据集模块介绍和使用案例

sklearn.datasets模块介绍 在scikit-learn中,可以使用sklearn.datasets模块中的函数来构建数据集。这个模块提供了用于加载和生成数据集的函数。 API Reference — scikit-learn 1.4.0 documentation 以下是一些常用的sklearn.datasets模块中的函数 load_iris() …

真是令人震惊!都已经2024年了,居然在撸货圈里还存在收徒的现象?

前不久发现了京东淘宝的正确购买方式之后 才明白一直想买的东西其实没有想象中的那么贵 而且这种购买方式谁都可以学会 首先要了解平台的规则和购买的方法!才能顺利买到!不然你看到了漏洞也不会撸,就看别人上车! 1.首先你要加…

你所不知道的关于库函数和系统调用的那些事

系统调用和库函数的区别 相信大家在面试或者刷面试题的时候经常能看到这样的问题,“简述一下系统调用和库函数的区别”。 系统调用是操作系统提供给用户的接口,能让用户空间的程序有入口访问内核。而库函数数一组标准函数,比如复合 POSIX 或…

【论文笔记】Lift-Attend-Splat: Bird’s-eye-view camera-lidar fusion using transformers

原文链接:https://arxiv.org/abs/2312.14919 1. 引言 多模态融合时,由于不同模态有不同的过拟合和泛化能力,联合训练不同模态可能会导致弱模态的不充分利用,甚至会导致比单一模态方法性能更低。 目前的相机-激光雷达融合方法多基…

react将选中本文自动滑动到容器可视区域内

// 自动滚动到可视区域内useEffect(() > {const target ref;const wrapper wrapperRef?.current;if (target && wrapperRef) {const rect target.getBoundingClientRect();const wrapperRect wrapper.getBoundingClientRect();const isVisible rect.bottom &l…

Vision Transfomer系列第一节---从0到1的源码实现

本专栏主要是深度学习/自动驾驶相关的源码实现,获取全套代码请参考 这里写目录标题 准备逐步源码实现数据集读取VIt模型搭建hand类别和位置编码类别编码位置编码 blocksheadVIT整体 Runner(参考mmlab)可视化 总结 准备 本博客完成Vision Transfomer(VIT)模型的搭建和flowers数…

2024机械工程师面试题

1.常用的机械画图软件有哪些 SolidWorks、Pro/e、CATIA、UG、Creo、CAD、inventor。CAXA电子图板. 2.第一视角是___,第三视角是___; 只要区别是:物体所处的位置不同。一般中国都使用第一视角的。 3.气缸属于_____执行元件,电磁…

Multisim14.0仿真(五十一)基于LM555定时器的分频器设计

一、1KHz脉冲设置: 二、555脉冲电路: 三、仿真电路: 四、运行仿真:

【Linux笔记】缓冲区的概念到标准库的模拟实现

一、缓冲区 “缓冲区”这个概念相信大家或多或少都听说过,大家其实在C语言阶段就已经接触到“缓冲区”这个东西,但是相信大家在C语言阶段并没有真正弄懂缓冲区到底是个什么东西,也相信大家在C语言阶段也因为缓冲区的问题写出过各种bug。 其…

【计算机视觉】万字长文详解:卷积神经网络

以下部分文字资料整合于网络,本文仅供自己学习用! 一、计算机视觉概述 如果输入层和隐藏层和之前一样都是采用全连接网络,参数过多会导致过拟合问题,其次这么多的参数存储下来对计算机的内存要求也是很高的 解决这一问题&#x…

2024.2.4

双向链表的头插 头删 尾插 尾删 //头插插入 Doublelink insert_head(Doublelink head,datatype element) {Doublelink screat_Node();s->dataelement;//判断是否有空链表if(NULLhead){heads;}else{s->nexthead;head->priors;heads;}return head; } //头删 Doublelink…

sql相关子查询

1.什么是相关子查询 相关子查询是一个嵌套在外部查询中的查询,它使用了外部查询的某些值。每当外部查询处理一行数据时,相关子查询就会针对那行数据执行一次,因此它的结果可以依赖于外部查询中正在处理的行。 2.为什么要使用相关子…

微信小程序之本地生活案例的实现

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…