CHS_08.2.3.6_1+生产者-消费者问题

CHS_08.2.3.6_1+生产者-消费者问题

  • 问题描述
  • 问题分析
  • 思考:能否改变相邻P、V操作的顺序?
  • 知识回顾

在这个小节中 我们会学习一个经典的进程同步互斥的问题

问题描述

在这里插入图片描述

并且尝试用上个小节学习的p v操作 也就是信号量机制来解决这个生产者消费者问题

问题的描述是这样的 在一个系统当中 有一组生产者进程和一组消费者进程

生产者进程每次生产一个产品并且放入缓冲区 那这缓冲区其实就是用来存放数据的

一片区域 我们可以把它理解为 一个一个小格子 每个缓冲区 也就是每一个小格子里面可以放一个产品

一个 也就是一坨数据 然后消费者呢 又会从这些缓冲区当中每次取出一个产品 也就是每次

取出一头数据 那生产者和消费者这两组进程他们会共享的使用

一个初始为空 大小为n的缓冲区 也就是有n个小格子可以放n个产品

那需要注意的是 这个缓冲区是有容量限制的 它只能放五个产品

那只有缓冲区没有满的时候 生产者才可以往这个缓冲区里放入产品

比如说像这个样子 因为缓冲区大小为五所以它最多只能放入五个产品 也就五个数据

那如果缓冲区此时已经满了 但是生产者进程还想尝试往里面写数据

那生产者进程必须阻塞等待 等这个缓冲区被取空的时候 他才可以往里边

写数据那有没有发现这个条件 就是我们上小节分析的同步问题

问题分析

在这里插入图片描述

也就是说 缓冲区没满的时候
生产者才能生产 这是一对一前一后的同步关系
另外呢

消费者进程 他会从这个缓冲区里取走这些产品 就是取走这些数据 就像这个样子

那当缓冲区没满的时候 也就是有空闲的缓冲区的时候 生产者就可以继续生产

也就是说 当一个消费者从缓冲区取走数据之后 如果此时有生产者是处于阻塞状态的 那么消费者进程应该把生产者进程给唤醒

让他重新回到就绪态 不过呢 生产者进程只是回到了就绪态 这并不意味着生产者进程会立即往里边写数据

所以接下来有可能是消费者进程继续往把这些缓冲区里的数据依次取走 那需要注意的是 只有缓冲区不空的时候 也就是说只有缓冲区里有数据 有产品的时候
消费者进程才可以从缓冲区里取走数据
不然的话消费者进程就必须阻塞等待

所以其实这个条件就是第二对的同步关系 也就是只有缓冲区没空的时候 或者说缓冲区里有产品的时候

消费者才可以取走数据 才可以消费 那最后一个条件是这个缓冲区 它是一种临界资源

各个进程必须互斥的访问缓冲区 那这我们来解释一下为什么必须互斥的访问缓冲区

那假设这个系统当中此时有两个生产者进程正在并发的运行 那么第一个生产者进程在检查缓冲区的时候发现 哎 这个缓冲区此时都是空的

所以我可以挑选其中的某一片往里面写入数据 与此同时 第二个生产者进程 它也并发的运行

他也发现此时这个缓冲区都是空的 因此他也会挑选其中的一块写入数据

那假设下面的这个生产者进程往这写了一篇数据 而上面这个生产者进程他自己挑选的也是这一片区域
那接下来这个生产者进程也往这片区域冲入自己的数据 那这样的话 他就会把前面那个进程的数据给覆盖掉

所以可以看到 如果各个进程同时访问缓冲区的话 那么有可能会出现一系列的问题 比如说刚才我们提到的数据覆盖
所以缓冲区这种资源 它是一种临界资源 各个进程必须互斥的访问

那么既然需要互斥的访问 这其实就是我们之前学过的互斥的问题 那刚才我们做的这一系列分析

找出了这个问题里边所隐含的一系列的同步和互斥的关系

那第二步我们需要做的是要根据各个进程操作的流程来确定pv操作的大致顺序

那其实很简单 就是我们上个小节中提到过的 前V后P 第一对关系是只有 当
缓冲区里有产品的时候 消费者进程 它才可以消费
另外呢 只有缓冲区没满的时候
生产者进程才可以生产
所以这样的两对一前一后的同步关系 我们就需要给他们分别设置一个同步信号量
在这里插入图片描述

并且在前面这个动作完成之后 需要对这个同步信号量执行一个V操作

在后面这个动作开始之前 需要对这个同步信号量执行一个p操作 那我们一会来解释一下这两个同步信号量 它在背后的含义

那这是这两对同步关系里边pv操作的一个大致顺序 那缓冲区里有产品

这个事件是生产者进程触发的 也就说 当一个生产者进程往缓冲区里放入一个产品 放入一个数据之后
他就需要对这个信号量执行一个V操作
而当消费者进程从缓冲区里取走数据之前
它需要对这个型号量执行一个p操作
那下面的这个同步关系也一样 那除了这两对同步关系之外 我们还需要实现对缓冲区这种临界资源的互斥访问

互斥的实现其实是很简单的 我们只需要设置一个互斥信号量 mutex 并且让它的初始值为一

然后在临界区的前面和后面分别对互斥信号量执行p v操作就可以了 那接下来我们应该做的就是要确定这些信号量的初始值

对于互斥信号量来说 一般初始值就是一就像我们刚才所说的那样 而对于同步信号量来说 它的初始值 我们要看它所对应的那种资源初始值是多少

那我们来看一下这两对同步关系所对应的这种资源到底是什么呢 那先来看上面这对同步关系

消费者进程 他在消费之前需要消耗什么资源呢 需要消耗的是产品 对吧

所以他的这个p操作其实是在申请一个产品 申请一个数据

因此 full这个同步信号量 它所对应的资源应该是产品的数量

也就是非空缓冲区的数量 而从题目当中给的条件 我们知道缓冲区刚开始都是空的

也就是说 刚开始产品的数量 产品这种资源的数量应该是零因此

full这个同步信号量的初始值 我们就把它设置为零表示刚开始没有产品这种资源
而这种资源只能由生产者进程
生产了之后来释放 那再来看第二个同步信号量empty

那我们知道 生产者进程每生产一个产品 就需要消耗一个空闲的缓冲区

因此 empty这个同步信号量 它所对应的资源就应该是空闲缓冲区这种资源

它的数量就是空闲缓冲区的数量 当一个消费者进程取走了产品 也就是

做了消费的动作之后 他就可以释放一个empty 也就是释放一个空闲的缓冲区

那从题目给的条件 我们知道 刚开始缓冲区都是空的 有n个缓冲区 因此空闲缓冲区它的初始值应该是n

所以这样我们就确定了这两个同步信号量的一个初始值 那接下来就是代码的实现

生产者进程 他要做的事情就是不断的生产一个产品 并且把产品放到缓冲区
在这里插入图片描述

而消费者进程 他要做的事情就是不断的从缓冲区里取走一个产品 并且使用这个产品消费这个产品

那通过刚才的分析 我们知道 生产者进程再把产品放入缓冲区之前需要申请一个空闲的缓冲区
因此 当他放入产品之前 需要对empty这个同步信号量执行一个p操作

而当他把产品放入缓冲区之后 其实就相当于产品这一种

资源的数量加一了 或者说非空缓冲区的数量加一了 因此当它放入产品之后 需要对这个信号量执行一个V操作

表示 增加一个这种资源 而消费者进程的分析也类似当他从缓冲区取走一个产品之前
需要先对这个full变量进行一个p操作 也就是要申请消耗一个产品这种资源

而当他取走了一个产品之后 空闲缓冲区的数量就会加一因此在这个操作之后 需要对empty进行一个v操作 表示要增加一个空闲缓冲区

那这样我们就实现了两对同步关系 另外 题目中要求缓冲区必须互斥的访问 它是临界资源 所以在访问缓冲区

前后分别要对这个互斥信号量执行p和v操作 用于实现对缓冲区的互斥访问

那从这个例子当中 我们可以看到 实现互斥的p v操作 它是在同一个进程内进行的

这个进程对临界区上了锁 而当他访问完临界区之后 又需要的对这个临界区进行解锁

所以 这个pv操作是在同一个进程的代码当中实现的 而同步关系就不一样了 我们会发现

这个进程 生产者进程里边是对full变量执行了v操作 而消费者进程里边是对full变量执行了p操作

也就是对同一个变量的p v 这两个操作是分别需要在不同的两个进程之间执行的

执行v操作的这个进程会唤醒相对应的执行p操作的这个进程

那如果结合刚才我们给的这个同步关系的前驱图 就会发现

这个代码其实完全可以根据这个前驱图的关系来得出 那这大家可以自己暂停来

体会一下前V后P 那接下来我们要思考的一个问题是

能否改变相邻的pv操作的顺序呢 也就说我们刚才是先对同步信号量执行了p

思考:能否改变相邻P、V操作的顺序?

在这里插入图片描述

再对互斥信号量执行了p 而我们现在尝试把它颠倒一下 先对这个互斥信号量执行p操作 再对同步信号量执行p操作 会发生什么情况呢

我们来看一下 假设此时这个缓冲区里边已经放满了产品 也就是empty等于零

full等于n 那此时如果生产者进程执行了一使empty的值变为了零

接着 他在执行二这一句 而此时由于empty的值已经是零了 所以生产者进程会被阻塞在二这一句 他需要等待empty这种资源

那这个时候发生了调度消费者进程 上处理机运行 那消费者进程在执行三这一句的时候

他会发现此时empty的值已经是零了 所以消费者进程他会被阻塞在三这一句

他需要等待生产者进程释放mutex 所以这就导致消费者进程也被阻塞了

那我们在捋一捋这种情况下 生产者进程 他在等待消费者进程对empty执行v操作

而消费者进程 他又在等待生产者进程对mutex执行v操作

也就说 他们在相互等待对方唤醒自己 而两个人其实都被阻塞 都在睡眠

而那这种情况就是我们之后会学习到的死锁 进程和进程之间发生了循环等待

被对方唤醒这样的一个神奇的情况 那发生了死锁之后 这两个进程就都不能往下执行 都不能推进下去了

那同样的 大家可以分析一下 当full等于零当empty等于n的时候 如果按341这样的顺序执行 是不是也会发生死锁

所以在这个地方 我们要强调的是实现互斥的这个p操作

一定要在实现同步的p操作之后 他们的顺序是不能颠倒的 如果颠倒的话 那么就有可能出现我们这所提到的死锁的问题

所以这是大家需要特别特别注意的地方 那接下来我们再思考一下 如果V操作的这个顺序颠倒的话 会不会出现问题呢

其实是不会的 因为V操作并不会导致任何一个进程阻塞 所以他们俩的顺序颠倒并不会发生这种循环等待的问题 所以两个V操作的顺序是可以交换的

那最后我们再来考虑一个问题 生产者生产一个产品和消费者使用一个产品 是否可以放到pv操作之间呢

其实逻辑上看是可以放到p v操作这里边的 但是如果我们把这两部分的处理都放到p v操作里边的话

那么就会导致临界区代码变得更长 也就是说 一个进程对临界区上锁的时间会增长
那这样的话 肯定不利于各个进程交替的来使用这个临界区资源

所以我们要让临界区的代码尽可能的短 因此逻辑上来看 把这两个部分放进去是没有问题的

但是实际上会对系统的效能产生影响 因此并不建议把这两个部分的操作放到p为操作里面

好的 那么这个小节当中 我们介绍了一个很经典的进程的同步互斥问题 就是生产者消费者模型

那我们根据这个问题 给出了做pv操作相关题目的一个解题思路 这个解题思路我们会在之后的

那些问题讲解当中在继续应用那生产者 消费者问题 它是一个互斥和同步的综合问题 既包含了互斥又包含了同步

但是对于我们初学者来说 比较困难的是 要发现这个题目当中隐含的两对同步关系

有时候是生产者进程在等待消费者进程 有的时候又是消费者进程需要等待生产者进程

所以这是这个题目当中的难点 它是两对不同的 一前一后的这种关系

所以我们相应的也需要给他们设置两对不同的信号量 但是对这些信号量的操作肯定都是遵循一个前V后P的 这个

原则那根据上个小节的讲解 相信这个地方已经不难理解了 另外还需要再强调一遍的是

知识回顾

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

实现互斥和实现同步是两个不同的p操作而实现互斥的

那个p操作一定要在实现同步的p操作之后 否则会导致死锁的问题

这个大家在暂停回忆一下 那么在接下来的讲解当中 我们会适当的加快一点点速度 我们不会在像之前一样每个同步信号量都来分析他背后的含义 这个需要大家自己去练习和体会

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【实战系列----消息队列 数据缓存】rabbitmq 消息队列 搭建和应用

线上运行图,更新不算最新版,但可以使用修改线程等补丁功能,建议使用新版本。 远程服务器配置图: 这个可以更具体情况,因为是缓存队列理所当然 内存越大越好,至于核心4核以上足够使用。4核心一样跑 这里主要是需要配置服…

麒麟系统—— openKylin 安装 Nginx

麒麟系统—— openKylin 安装 Nginx 一、准备工作1. 确保麒麟系统 openKylin 已经安装完毕。 二、下载 nginx三、解压与运行解压检查与编译安装编译运行 四、配置加入到服务中加入环境变量nginx 配置文件 五、常用命令 Nginx 是一款高性能的 HTTP 和反向代理服务器&#xff0c…

Android super.img解包和打包指南(含工具下载lpunpack、lpmake、lpdump)

本文所有命令均需要在linux 上执行 一、解包 1、将Android sparse image格式的super.img转成二进制文件 $ sudo apt install android-sdk-libsparse-utils $ simg2img super.img super.img.bin 2、下载工具lpunpack 和lpmake、lpdump 以及其依赖库 下载地址:https://downl…

C语言实现12种排序算法

1.冒泡排序 思路:比较相邻的两个数字,如果前一个数字大,那么就交换两个数字,直到有序。 时间复杂度:O(n^2),稳定性:这是一种稳定的算法。 代码实现: void bubble_sort(int arr[],…

在Debian11安装Proxmox VE 7及设置GPU工作环境

我们在测试大模型,深度学习的过程中,可能遇到的情况就是不断的清空系统,避免老系统中安装的软件对测试系统造成影响,导致测试结果不准确或莫名报错。今天为小伙伴们介绍使用PVE7做成一个人工智能开发和测试的平台,你可…

Day40 0-1背包问题

0-1背包问题 小明是一位科学家,他需要参加一场重要的国际科学大会,以展示自己的最新研究成果。他需要带一些研究材料,但是他的行李箱空间有限。这些研究材料包括实验设备、文献资料和实验样本等等,它们各自占据不同的空间&#x…

户外没有电源和网络,但需要安装监控系统,怎么办?太阳能智能监控系统给你解决

近期有粉丝给小编求助:需要在没网没电的户外进行智能监控的安装,不知道如何解决。收到粉丝的问题,小编立刻联系了技术人员给出方案。针对野外、户外等场景只需使用太阳能供电模组4G摄像机视频监控EasyCVR平台智能分析网关V4的架构&#xff0c…

【2023地理设计组一等奖】基于机器学习的地下水仿真与时空分析

作品介绍 1 设计思想 1.1 作品背景 华北平原是我国最重要的粮棉产地之一,然而近年来农业的低效用水以及过度压采正逐步加剧其地下水资源的紧张性,为经济可持续发展带来重大风险。而地下水动态变化与人为干预、全球气候波动呈现出高度相关性,因此,地下水的仿真模拟对保障粮…

网络编程套接字(2)

TCP 简单的TCP网络程序服务端创建套接字 服务端绑定服务端监听服务端接收连接测试服务端处理请求客户端创建套接字客户端连接服务器客户端连接服务器单执行流的服务器客户端为什么会显示连接成功? 多进程版的TCP网络程序让孙子进程提供服务 多线程版的TCP网络程序 简…

力扣之2629.复合函数(reduceRight )

/*** param {Function[]} functions* return {Function}*/ var compose function(functions) {return function(x) {return functions.reduceRight((result, func) > func(result), x);} };/*** const fn compose([x > x 1, x > 2 * x])* fn(4) // 9*/ 说明&#x…

Win10的蓝牙和其他设备没有蓝牙开关选项

一、问题背景 虽然蓝牙驱动并没有问题,但是Win10的蓝牙和其他设备却没有显示蓝牙开关的选项。这导致笔记本电脑无法使用蓝牙连接设备。如下图所示: 添加蓝牙或其他设备的标题下面本来应该有一个蓝牙开关,但是笔记本电脑却并没有显示这样的…

k8s安装dashboard报错CrashLoopBackOff

报错信息 使用kubectl get pods -A查看集群,出现错误: kubernetes-dashboard kubernetes-dashboard-xxxxxxxxxx6-2qrst 0/1 CrashLoopBackOff 6 15m查看日志后,发现原因: panic: Get "https://10…

搭建自己的私服 maven 仓库

申明:本文章所使用docker-compose配置文件纯属学习运用,非商用如有雷同请联系本人协调处理。 一、配置docker-compose.yml文件 # 指定docker-compose的版本 version: 3 services: nexus: container_name: nexus_container image: sonatype/nex…

经典mysql实操和行专列操作

1.删除除了学号字段以外,其它字段都相同的冗余记录,只保留一条!(也就是要删除王五和赵六中一条重复数据只留一条) 要求的预期效果: 原始数据创建表结构: CREATE TABLE tb_student (id int(16) NOT NULL,na…

C#验证字符串是否大写、小写,正则表达式vs用Char.IsUpper和Char.IsLower方法遍历字符数组

目录 一、使用的方法 1.正则表达式 2.用Char.IsUpper或Char.IsLower方法 二、源代码 1.源码 2.生成效果 一、使用的方法 1.正则表达式 正则表达式“^[A-Z]$”,其中[A-Z]表示匹配一个到多个大写字母。 正则表达式“^[a-z]$”,其中[a-z]表示匹配一个…

C# wpf 字体图标预览,html字符与unicode转换

在进行wpf 开发工作过程中遇到字体图标无法预览的问题&#xff0c;特此记录。 1、把需要预览的字体文件上传到网站上进行转换 Create Your Own font-face Kits Font Squirrel2、下载文件后进行解压。 3、找到 Glyph Chart 查看字体html字符编码4、在wpf中直接使用即可 <…

【JAVA】单例模式的线程安全性

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;JAVA ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 正文 我的其他博客 正文 老生常谈的问题了&#xff0c;首先要说的是单例模式的线程安全意味着&#xff1a;某个类的实例在多线程环境 下只会被…

前端框架简介及Vue3项目起步基础配置

前端框架简介及Vue3项目起步基础配置 前端框架简介Vue1.1 Vue脚手架1.1.1 使用vue-cli创建vue2项目1.1.2 使用create-vue创建vue3项目1.1.3 项目起步-配置别名路径联想提示1.1.4 项目起步-elementPlus引入1. 安装elementPlus和自动导入插件2. 配置自动按需导入3. 测试组件 1.1.…

DRV8313和L298N都是电机驱动,一个是驱动三相FOC无刷直流电机的,一个是驱动有刷电机,使stm32控制无刷电机简单入门知识

DRV8313和L298N都是电机驱动器&#xff0c;但它们之间存在一些关键的区别&#xff1a; DRV83131&#xff1a; 由德州仪器&#xff08;TI&#xff09;制造。 具有集成的场效应晶体管&#xff08;FET&#xff09;。 最大电压为65V。 峰值电流为3A。 适用于三相电机驱动。 L298N…

中科大计网学习记录笔记(三):接入网和物理媒体

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…