当前位置: 首页 > news >正文

进程与线程:09 进程同步与信号量

课程引入:进程同步与信号量

接下来这节课开始,我们再开始讲多进程图像。讲多进程图像的下一个点,前面我们讲清楚了多进程图像要想实现切换,调度是如何做的。同时,多个进程放在内存中,就会存在多进程合作的情况,而这种合作应该是合理有序的,这部分内容就是进程同步——让多进程之间的合作变得合理有序。那么怎么来实现这种合理有序呢?就要靠信号量。这堂课我们要讲清楚为什么会有信号量,以及如何依靠信号量来实现多个进程推进的合理有序,即同步。

现实案例:进程合作与同步的重要性

首先从一个例子看起,在现实社会中,司机和售货员就如同两个进程,司机的动作是启动车、运行、到站停车,售货员的动作是关门、售票、开门 ,他们各自有一套执行流程。这两个进程在同一台车上,为完成车辆的合理有序行驶,必须进行合作。在这里插入图片描述

  • 不合作的后果:如果二者执行顺序没有约束,比如售票员开门售票时,司机启动车辆,就会造成严重后果。所以,司机启动车辆不能随意进行,需要等待一个信号,比如售票员卖完票告知可以走了;同样,售票员开门也不是随便进行的,要等车辆到站停车的信号。
  • 合作与同步的体现:司机等待售票员关门的信号再启动车辆,售票员在车辆到站后得到停车信号才开门,这就体现了进程间的合作。一个进程等待信号,另一个进程在合适的时候发送信号,从而使多个进程按照一定顺序向前推进,这就是同步。每个进程有自己的执行程序,但不是每条程序都能随便执行,有时需要停下来等待信号,当收到信号后再继续执行,这就是多进程合理有序的合作与同步。

技术案例:生产者 - 消费者模型中的同步问题

接下来以生产者 - 消费者模型为例进一步说明。有一个共享缓冲区,生产者不断向里放内容,每放完一个 counter 加一;消费者不断从里取内容,每取出一个 counter 减一 ,这是典型的多进程合作场景。在这里插入图片描述

  • 同步需求:在这个模型中,也需要合理有序的推进。当生产者发现 counter 等于缓冲区大小 buff size ,即缓冲区满了,就不能继续放入,必须等待;同理,当缓冲区为空时,消费者也应该停止。在这里插入图片描述
    所以,进程同步的关键在于分析进程在哪些地方该停、什么时候该走。在生产者 - 消费者模型中,缓冲区满时生产者停,消费者消费后产生空闲缓冲区,就给生产者发信号让其继续;缓冲区空时消费者停,生产者生产后给消费者发信号。在这里插入图片描述

  • 信号的局限性:仅依靠信号存在问题。例如,当缓冲区满时,生产者 p1 尝试放入会因 counter 等于 buff sizesleep ,之后另一个生产者 p2 进来,同样会 sleep 。此时若消费者执行一次循环,取出一个内容, counter 变为 buff size - 1 ,消费者判断缓冲区未满,认为无人等待缓冲区,不会再发信号唤醒 p2 ,导致 p2 永远无法唤醒。这说明单纯依靠 counter 进行语义判断不足,不仅需要知道缓冲区中空闲个数,还需知道有多少进程在睡眠等待。

信号量的引入与原理在这里插入图片描述

为解决上述问题,引入信号量。信号量是一个整数,它能记录更多信息。例如信号量等于 -2 ,表示有两个进程在等待 。当消费者执行时,发现信号量为 -2 ,就会唤醒阻塞队列头部的进程(如 p1 ),同时信号量变为 -1 ;再次执行,唤醒 p2 ,信号量变为 0
在这里插入图片描述

  • 信号量的语义:信号量为负数时,表示有进程阻塞,其绝对值代表等待进程的数量;为 0 时,表示没有进程等待,但也没有可用资源;为正数时,表示有可用资源,数值代表资源数量 。如消费者执行使信号量变为 1 ,表示还有一个空闲缓冲区,此时若有新的生产者 p3 来,无需睡眠可直接执行,执行后信号量变为 0 ;再有生产者来,信号量变为 -1 ,该生产者需阻塞等待。
  • 基于信号量的进程决策:进程根据信号量的值决定是否等待或唤醒其他进程。生产者申请使用资源(如空闲缓冲区)时,若信号量为负或零,说明资源不足或已用完,需等待;消费者释放资源(产生空闲缓冲区)时,若信号量为负,说明有进程在等待,需唤醒一个等待进程。

信号量与资源等待的关系

我们可以根据这个习题来回答,对于一种数量为八的资源,思考进程等待的原因。进程等待肯定是因为申请资源时没有可用资源了。在进程同步中,竞争合作体现为走走停停,而“停滞”是其中的核心,所以明确进程何时等待至关重要。当一个进程访问资源却发现没有资源时,就会进入等待状态。在这里插入图片描述

这种资源对应的信号量初值应该设为八,这表示初始状态下可以使用八个该资源。当信号量的值变为零时,意味着没有资源剩余,若值再变为负数,进程就需要等待。当信号量的值为二时,说明还有两个资源可供使用,此时没有进程在等待该资源;而当信号量的值为 -2 时,则表示有两个进程正在等待该资源 。

通过信号量的值,我们能够判断系统中有多少进程在等待资源,以及还有多少资源可供使用。基于这样的判断,我们可以控制进程的执行与暂停,从而实现进程同步,其核心就在于依据信号量的值来判断进程何时该“走”、何时该“停”。

信号量的核心概念与操作

  1. 信号量的定义与作用
    进程之间的同步是多个进程走走停停的合理有序推进,判断何时停要看信号量的值。当信号量为负值或 0 时,进程申请信号量会变成负值,此时进程等待;其他进程根据信号量的值,若为负,在释放信号量时进行唤醒操作;若为正,直接累加,无需发信号。

  2. 信号量的实现方式
    在编程实现中,判断进程是否需要等待资源是通过调用函数来完成的,这就涉及到信号量的具体实现。信号量的核心是一个整数,它记录着资源的相关信息。为了方便用户操作,我们通过定义 PV 操作这两个接口函数来实现对信号量的控制。在这里插入图片描述

    • P 操作:当进程想要申请资源,判断自己是否应该暂停时,就调用 P 操作。以 P(sem) 为例,执行该操作时,首先将信号量 sem 的值减 1。这是因为进程申请资源,相当于资源数量减少。如果减 1 后信号量的值小于 0,说明在本次申请之前,资源要么已经没有剩余(值为 0),要么已经处于供不应求的状态(值为负),当前进程无法获得资源,此时进程就会进入睡眠状态,并被放入与该信号量相关联的阻塞队列中。例如,生产者进程每次使用空闲缓冲区时,就需要对空闲缓冲区对应的信号量执行 P 操作,以此判断是否有空闲缓冲区可供使用,如果没有则进入等待。
    • V 操作:有进程等待,就需要有唤醒操作,这就是 V 操作的作用。当进程释放资源时,会调用 V 操作。执行 V 操作时,将信号量的值加 1 ,这表示资源数量增加。如果加 1 后信号量的值仍然小于等于 0,说明在释放资源之前,有进程在等待该资源(信号量为负表示等待进程数,为 0 表示刚有进程等到资源),此时就需要调用 wake up 函数,从阻塞队列中唤醒一个进程;如果加 1 后信号量的值大于 0 ,则表示没有进程在睡眠等待,无需进行唤醒操作。比如消费者进程产生空闲缓冲区后,就会对相应的信号量执行 V 操作。
    • 系统调用:由于信号量操作涉及到进程睡眠等在内核态完成的操作,所以 PV 操作需要做成系统调用,这样上层应用程序就能通过调用系统调用来使用信号量。其中,P 操作源于荷兰语“test”,表示测试是否需要阻塞;V 操作源于荷兰语“increment”,表示增加资源数量,进而实现唤醒等待进程的功能 。

信号量解决生产者 - 消费者问题

利用信号量及其 PV 操作,可以有效解决生产者 - 消费者问题,实现进程间的同步与合作。在解决该问题时,关键在于分析生产者和消费者何时会暂停,并据此定义相应的信号量。在这里插入图片描述

  1. 分析生产者与消费者的等待条件
    • 生产者:当缓冲区满时会停,所以定义一个信号量 empty 表示空闲缓冲区个数,初值为 buff_size 。生产者每次操作前先执行 P(empty) ,测试 empty 是否为 0 ,即缓冲区是否满,若满则等待。当消费者释放空闲缓冲区时,执行 V(empty) 增加 empty 的值 。
    • 消费者:当缓冲区没有内容时会停,定义一个信号量 full 表示已生产内容的个数,初值为 0 。消费者每次操作前先执行 P(full) ,测试 full 是否为 0 ,即是否有内容,若无则等待。当生产者生产内容后,执行 V(full) 增加 full 的值 。
  2. 互斥信号量实现共享资源互斥访问
    共享缓冲区(可视为文件)的操作需要互斥,即同一时刻只能有一个进程访问。定义一个互斥信号量 mutex ,初值为 1 。生产者和消费者在访问共享缓冲区前,先执行 P(mutex) ,若 mutex 等于 1 ,则变为 0 ,进程进入;访问结束后执行 V(mutex) 释放资源,使其他进程可以进入 。

通过上述信号量的设置以及 PV 操作的合理运用,依据信号量数值所代表的语义,准确判断进程是否需要睡眠或唤醒其他进程,从而实现了生产者和消费者之间执行过程的合理有序,最终解决了进程同步问题,实现了二者的合作 。

相关文章:

  • QLineEdit增加点击回显功能
  • Python 字典键 “三变一” 之谜
  • WebGIS 开发中的数据安全与隐私保护:急需掌握的要点
  • 带格式的可配置文案展示
  • 典籍指数问答模块回答格式修改
  • 深入浅出:C++数据处理类与计算机网络的巧妙类比
  • 嵌入式学习--江协51单片机day5
  • PostgreSQL 配置设置函数
  • SQL Server中delete table和truncate table删除全表数据哪个快?
  • 文件操作: File 类的用法和 InputStream, OutputStream 的用法
  • 基于SSM实现的健身房系统功能实现十六
  • 操作系统导论——第29章 基于锁的并发数据结构
  • 代理IP的核心原理:从请求转发到匿名性实现
  • 如何界定合法收集数据?
  • 消息~组件(群聊类型)ConcurrentHashMap发送
  • 嵌入式学习笔记 - 头文件重复包含问题
  • React百日学习计划——Deepseek版
  • C++ 基础知识
  • Codeforces Round 997 (Div. 2)
  • JAVA实战开源项目:乐享田园系统 (Vue+SpringBoot) 附源码
  • 上海市重大工程一季度开局良好,崇明线等按既定计划加快建设
  • 足球少年郎7月试锋芒,明日之星冠军杯构建顶级青少年赛事
  • 阿坝州委书记徐芝文已任四川省政府党组成员
  • 中央结算公司:减免境外央行类机构账户开户费用
  • 多元史料下的“西狩”叙事——《“庚子西狩”中外资料六种》解题
  • 5年建成强化城市核心功能新引擎,上海北外滩“风景文化都是顶流”