第二章-进程同步与互斥
进程同步与互斥
什么是进程同步:
同步即为直接制约关系,指的是为完成某种任务而建立的两个或多个进程,这些要在某个位置上协调他们的工作次序而产生的制约关系。进程间的直接制约关系来源于它们之间的相互合作。
什么是进程互斥
对临界资源的访问,需要互斥的进行。即为同一时间段只能允许一个进程访问该资源;间接制约关系
四个部分
- 进入区:检查是否可以进入临界区,若可以进入,需要“上锁”
- 临界区:访问临界资源的那段代码
- 退出区:负责“解锁“
- 剩余区:其余代码部分
需要遵守的原则
空闲上进
忙则等待
有限等待
让权等待
进程互斥的软件实现方法
-
单标志法
- 在进入区只做:检查,不上锁
- 在退出区把临界区的使用权转交给另一个程序
(相当于在退出区既给另一进程“解锁”,又给自己 上锁;) - 主要问题:不遵守“空闲让进”原则
-
双标志先检查法
- 在进入区先”检查“后”上锁“,退出区”解锁“
- 主要问题:不遵守:忙则等待“原则
-
双标志后检查法
- 在进入区先”加锁“后”检查“,退出区”解锁“
- 主要问题:不遵循:空闲让进、有限等待“原则,可能导致”饥饿“
-
Peterson
- 在进入区“主动争取-主动谦让-检查对方是否想进入、乙方是否谦让“
- 主要问题:不遵循“让权等待”原则,会发生“忙等”
进程互斥的硬件实现方法
中断屏蔽方法
- 利用开/关中断指令 实现
- 优点:简单、高效
缺点:不适用于多处理机;只是用操作系统内核进程,不适用于用户进程(因为开/关中断指令只能运行在内核态,这组指令如果能让用户随意使用很危险)
1 TestAndSet指令(简称:TS、TSL指令)
-
TSL指令是用硬件实现的,执行的过程不允许被中断,只能一气呵成。
-
-
不满足让权等待原则
2 Swap指令(EXchange指令)简称XCHG指令
-
用硬件实现,执行不允许被打断,只能一气呵成。
-
实现:
-
优缺点:
进程的互斥之 锁
1 互斥锁
- 解决临界区最简单的工具
- 每个互斥锁有一个布尔变量available,表示锁是否可用-调用acquire()会成功,且锁不再可用;当程序试图获取不可用的锁时会被阻塞,直到锁被释放;
- acquire() 、release()的执行必须是原子操作
- 互斥锁的主要缺点是忙等待;
需要连续忙等待的锁可称为“自旋锁(spin lock)“ 如:TSL指令、swap指令、单标志法 - 特性:
信号量机制
整型信号量
记录型信号量
用信号量机制实现同步互斥
-
实现进程互斥
-
实现进程同步
-
-
例题:
-
-
实现进程的前驱关系
生产者-消费者问题
系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。
生产者、消费者共享一个初始为空、大小为n的缓冲区。
只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待。
只有缓冲区不空时,消费者才能从中取出产品,否则必须等待。
缓冲区是临界资源,各进程必须互斥地访问
如何实现
-
-
若改变相邻P、V操作顺序会怎么样
P、V操作解题思路
多生产者 多消费者问题
-
例题:桌子上有一只盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子空时,爸爸或妈妈才可向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果。
-
-
吸烟者问题
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷
起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草、
第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种材料,供应者每次将两种材料放桌
子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供
应者就会放另外两种材料再桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)
本质上这题也属于“生产者-消费者”问题,更详细的说应该是“可生产多种产品的单生产者-多
消费者”
实现:
读者写者问题
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。
因此要求:
①允许多个读者可以同时对文件执行读操作;
②只允许一个写者往文件中写信息;
③任一写者在完成写操作之前不允许其他读者或写者工作;
④写者执行写操作前,应让已有的读者和写者全部退出。
分析:
两类进程:写进程、读进程
互斥关系:写进程—写进程、写进程—读进程。读进程与读进程不存在互斥问题。
实现:
哲学家进餐问题
一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学
家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,
才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲
学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。
分析:
如何解决死锁问题
- ①可以对哲学家进程施加一些限制条件,比如最多允许四个哲学家同时进餐。这样可以保证至少有一个哲学家是可以拿到左右两只筷子的
- ②要求奇数号哲学家先拿左边的筷子,然后再拿右边的筷子,而偶数号哲学家刚好相反。用这种方法可以保证如果相邻的两个奇偶号哲学家都想吃饭,那么只会有其中一个可以拿起第一只筷子,另一个会直接阻塞。这就避免了占有一支后再等待另一只的情况。
- ③仅当一个哲学家左右两支筷子都可用时才允许他抓起筷子。
管程
为什么要引入管程?
- 1 信号量机制的问题:编写程序困难、易出错
- 是否可以设计一种机制,让程序员写程序时不需要再关注复杂的PV操作,让编写代码更轻松?
- 1973年,Brinch Hansen 首次在程序设计语言 (Pascal)
中引入了“管程”成分——一种高级同步机制
管程定义和基本特征
- 管程是一种特殊的软件模块,有以下部分组成
- 1 局部于管程的共享数据结构说明;
- 2 对该数据结构进行操作的一组过程‘
- 3 对局部于管程的共享数据设置初始值的语句;
- 4 管程有一个名字;
-
基本特征
- 1 局部于管程的数据只能被局部于管程的过程所访问
- 2 一个进程只有通过调用管程的过程才能进入管程访问共享数据;
- 3 每次仅允许一个进程在管程内执行某个内部过程;
例子:用管程解决生产者消费者问题
- 引入管程的目的无非就是要更方便地实现进程互斥和同步。
- 需要在管程中定义共享数据(如生产者消费者问题的缓冲区)
- 需要在管程中定义用于访问这些共享数据的“入口”——其实就是一些函数(如生产者消费者 问题中,可以定义一个函数用于将产品放入缓冲区,再定义一个函数用于从缓冲区取出产品)
- 只有通过这些特定的“入口”才能访问共享数据
- 管程中有很多“入口”,但是每次只能开放其中一个“入口”,并且只能让一个进程或线程进 入(如生产者消费者问题中,各进程需要互斥地访问共享缓冲区。管程的这种特性即可保证一
个时间段内最多只会有一个进程在访问缓冲区。注意:这种互斥特性是由编译器负责实现的, 程序员不用关心)- 可在管程中设置条件变量及等待/唤醒操作以解决同步问题。可以让一个进程或线程在条件变量 上等待(此时,该进程应先释放管程的使用权,也就是让出“入口”);可以通过唤醒操作将 等待在条件变量上的进程或线程唤醒。
程序员可以用某种特殊的语法定义一个管程(比如: monitor ProducerConsumer …… end monitor;),
之后其他程序员就可以使用这个管程提供的特定“入口”很方便地使用实现进程同步/互斥了。
- Java 中类似于管程的机制
- Java 中,如果用关键字 synchronized 来描述一个函数,那么这个函数同一时间段内只能被一个线程调用
总结
本文来自博客园,作者:taotooler,转载请注明原文链接:https://www.cnblogs.com/taolo/p/17225861.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!