并发与并行*
- 并发与并行的区别:
并发 | 并行 | |
---|---|---|
系统 | 单处理器多道程序系统 | 多处理器系统 |
含义 | 进程被交替地执行 | 重叠执行进程 |
- 并发会产生诸多问题根源在于进程的相对执行速度不可预测
- 互斥与同步的区别:
互斥:
多个进程在同一时刻只有一个进程能够进入临界区同步:
多个进程进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系- 互斥解决了多进程对临界区使用的问题,但是它没有解决多进程协同工作的问题
- 同步就是多进程间在一些关键点上可能需要互相等待与互通消息,这种相互制约的等待与互通信息称为进程同步
- 同步是一种更复杂的互斥,而互斥是一种特殊的同步
- 竞争条件:
竞争条件:
在多个进程或线程读写共享数据时,最终结果取决于多个进程或线程的指令执行顺序- 操作系统要保证一个进程的
功能
和输出结果
必须与执行速度无关(相对于其他并发进程的执行速度
)
进程的交互
- 进程之间可能是相互独立的,也可能是由交互性的
(1)进程之间相互不知道
对方的存在(竞争
)
(2)进程间接知道
对方的存在(通过共享合作
)
(3)进程直接知道
对方的存在(通过通信合作
)
- 进程间的资源竞争:
- 竞争进程间没有任何信息交换,但一个进程的执行可能会影响到竞争进程的行为
临界资源:
一次仅允许一个进程使用的资源- 对临界资源的访问必须互斥地进行
临界区:
在每个进程中,访问临界资源的那段代码
互斥:
当一个进程进入临界区使用临界资源时,另一个进程必须等待,当占有临界资源的进程退出临界区后,另一个进程才允许去访问此临界资源- 实施互斥产生了两个额外的问题:
死锁
和饥饿
- 互斥的要求:
必须强制实施互斥:
在与相同资源或共享对象的临界区有关的所有进程中,一次只允许一个进程进入临界区
- 一个在非临界区停止的进程不能干涉其他进程
- 绝不允许出现需要访问临界区的进程被无线延迟的情况,即不会死锁或饥饿
- 没有进程在临界区中时,任何需要进入临界区的进程必须能够立即进入
- 对相关进程的执行速度和处理器的数量没有任何要求和限制
- 一个进程驻留在临界区中的时间必须是有限的
- 互斥的实现方法:
软件方法
:让并发执行的进程承担这一责任硬件方法:
中断禁用
、专用机器指令
在操作系统/程序设计语言中提供某种级别的支持
:信号量
、管程
、消息传递
硬件方法
中断禁用
- 对于
并发执行的单处理器系统
,由于临界区不能被中断,因此可以实现互斥- 对于
并行执行的多处理器系统
,禁用中断并不能保证互斥- 由于处理器被限制得只能交替执行程序,因此执行效率会明显下降
专用机器指令
- 在硬件级别上,对存储单元的访问排斥对相同单元的其他访问
- 整个比较和交换功能按
原子操作执行
,即它不接受中断
- 忙等待(自旋等待):
- 定义: 进程在得到临界区访问权之前,它只能继续执行测试变量的指令来得到访问权,除此之外不能做任何其他事情
- 优点:
- 适用于
单处理器
或共享内存的多处理器
上的任意数量的进程- 简单且易于证明
- 可用于支持多个临界区,每个临界区可以用它自己的变量定义
- 缺点:
使用了忙等待:
当一个进程正在等待进入临界区时,它会继续消耗处理器的时间可能饥饿:
当一个进程离开临界区且有多个进程正在等待时,选择哪个等待进程是任意的,因此某些进程可能会被无限地拒绝进入可能死锁:
(1) P 1 P1 P1进入临界区后, P 1 P1 P1被具有更高优先级的进程 P 2 P2 P2中断
(2)若 P 2 P2 P2试图访问同一资源,由于互斥机制,它将被永远拒绝,因此会进入忙等待,但由于 P 1 P1 P1优先级低于 P 2 P2 P2它永远不会被调度执行
信号量
- 定义: 用于进程间传递信号的一个
整数值
- 操作:
初始化
、递减
、递增
(1)这三种操作都属于原子操作
(2)递减:
阻塞一个进程
(3)递增:
解除一个进程的阻塞- 要通过信号量 s s s发送信号,进程须执行原语 s e m S i g n a l ( s ) semSignal(s) semSignal(s),使信号量+1,若值 ≤ 0 \leq0 ≤0,则被 s e m W a i t ( s ) semWait(s) semWait(s)操作阻塞的进程
解除阻塞
- 要通过信号量 s s s接收信号,进程须执行原语 s e m W a i t ( s ) semWait(s) semWait(s),使信号量-1,若值 < 0 <0 <0则
阻塞
执行 s e m W a i t ( s ) semWait(s) semWait(s)的进程
- s.count:
- s . c o u n t > = 0 s.count >= 0 s.count>=0:
s.count
表示还可以执行 s e m W a i t ( s ) semWait(s) semWait(s)而不会被阻塞的进程数(可用资源数
)- s . c o u n t < 0 s.count < 0 s.count<0:
s.count
表示阻塞队列中进程的个数(被阻塞进程数
)
- 二元信号量:
- 定义: 只取
0
值和1
值的信号量- 基本原理:
(1)初始化:
0 或 1
(2)semWaitB
检查信号的值,若为0
,进程被阻塞,若为1
,继续执行该进程
(3)semWaitB
检查是否有任何进程在该信号上受阻,若有
:唤醒受阻进程,若无
:值设置为1
- 互斥锁:
二元信号量和互斥量的关键区别:
为互斥量加锁(设定值为0
)和为互斥量解锁(设定值为1
)的进程必须是同一进程- 可能由某个进程对二元信号量进行
加锁操作
,而由另一个进程为其解锁
- 移出顺序:
- 强信号量: 采取
先入先出
策略移出被阻塞进程的信号量,反之则为弱信号量
信号量解决互斥
生产者/消费者问题
- 问题描述:
- 有一个或多个生产者生产某种类型的数据,并放置在缓冲区中
- 有一个消费者从缓冲区中取数据,每次取一项
- 系统保证避免对缓冲区的重复操作,即在任何时候只能有一个主体(生产者或消费者)可访问缓冲区
- 当缓存已满时,生产者不会向其中添加数据
- 当缓存为空时,消费者不会从中移走数据
- 缓冲区无限:
- 假设缓冲区是无限的,且是一个线性的元素数组
- 操作
semSignal(s)
或semSignal(n)
被互换,不会影响程序,因为消费者在继续进行之前必须在两个信号上等待- 操作
semWait(s)
或semWait(n)
被互换会产生致命的错误,若缓冲区为空(n.count=0
)时消费者曾进入过临界区,那么任何一个生产者都不能继续想缓冲区中添加数据项,系统发生死锁。
- 缓冲区有限:
信号量的实现
- 由于
semWait
和semSignal
操作都相对较短,因此涉及的忙等待
时间量非常小
管程
- 使用信号量设计一个正确的程序是很困难的
- 组成:
一个或多个过程
、一个初始化序列
、局部数据
(1)局部数据只能被管程的过程访问,任何外部过程都不能访问
(2)一个进程通过调用管程的一个过程进入管程
(3)在任何时候只能有一个进程在管程中执行,调用管程的任何其他进程都被阻塞,以等待管程可用- 管程使用
条件变量
来支持同步,这些条件变量包含在管程中,并且只有在管程中才能被访问。
- 条件变量:
- 定义: 一种数据类型,用于阻塞进程或线程,直到特定的条件为真
(1)cwait(c):
调用进程的执行在c
上阻塞
(2)csignal(c):
恢复执行在cwait
之后因某些条件而被阻塞的进程- 如果管程中的一个进程发信号,但没有在这个条件变量上等待的任务,则丢弃这个信号
- 一次只有一个进程可以进入管程中,其他试图进入管程中的进程被阻塞并加入等待管程可用的进程队列中
- 当一个进程在管程中时,它可能会通过发送
cwait(x)
把自己暂时阻塞在条件x
上,随后被放入等待条件改变以重新进入管程的进程队列中,在cwait(x)
调用的下一条指令开始恢复执行- 若在管程中执行的一个进程法线条件变量
x
发生了变化,则它发送csignal(x)
,通知相应的条件队列条件已改变
- 信号量 vs 管程
- 对于管程,它构造了自己的互斥机制
- 使用信号量的情况下,执行互斥和同步都是程序员负责
- 管程的优点:
- 所有的同步机制都被限制在管程内部,因此不但易于验证同步的正确性,而且易于检测出错误
- 若一个管程被正确辨析,则所有进程对受保护资源的访问都是正确的
- 对于信号量,只有当所有访问资源的进程都被正确地编写时,资源访问才是正确的
消息传递
- 消息传递的实际功能以一对原语的形式提供:
(1)send(destination, message)
:发送信息
(2)receive(source, message)
:接收原语- 根据
send
和receive
阻塞与否可分为:
(1)阻塞send,阻塞receive
(2)无阻塞send,阻塞receive
(3)无阻塞send, 无阻塞receive
- 寻址:
- 直接寻址:
send:
原语包含目标进程的标识号receive:
(1)进程显式地指定源进程
(2)不可能指定所期望的源进程(隐式寻址),eg:
打印机服务器进程将接受各个进程的打印请求
- 间接寻址:
- 消息不直接从发送者发送接收者,而是发送到一个共享数据结构(
信箱
)中信箱
由临时保存消息的队列组成- 间接寻址通过解除发送者和接收者之间的耦合关系,可更灵活地使用消息
- 消息格式:
- 互斥:
- 假设使用阻塞
receive
原语和无阻塞send
原语,且一组并发进程共享一个信箱box
- 若有一条消息,则它仅传递给一个进程,而其他进程被阻塞
- 若消息队列为空,则所有进程都被阻塞
- 一条消息可用时,一条消息可用时,仅激活一个阻塞进程,并得到这条消息
- 生产者/消费者问题:
读者/写者问题
- 问题描述:
- 存在一个多进程共享的数据区,该数据区可以是一个文件或一块内存空间
- 有些进程(
reader
)只读取这个数据区中的数据- 有些进程(
writer
)只往数据区中写数据- 同时满足以下条件:
(1)任何数量的读进程可同时读这个文件
(2)一次只有一个写进程可以写文件
(3)若一个写进程正在写文件,而写进程需要排斥其他所有进程,包括读进程和写进程
读者优先
- 读进程具有优先权,一个读进程开始访问数据区时,只要有一个进程在读,就为读进程保留对这个数据区的控制权,因此写进程有可能处于饥饿状态
写者优先
- 信号量
rsem:
至少有一个写进程准备访问数据区时,用于禁止所有的读进程- 变量
writecount:
控制rsem
的设置- 信号量
y:
控制writecount
的更新