操作系统原理(十)之信号量与管程
操作系统原理
信号量也是实现同步互斥的方法之一。
定义
- 信号量是操作系统提供的一种协调共享资源访问的方法。用信号量表示系统资源的数量。
- 信号量是一种抽象的数据类型
由一个整型(sem)变量和两个原子操作组成
P()(Prolaag(荷兰语尝试减少))
sem减1
如sem<0,进入等待,否则继续
V()(Verhoog(荷兰语增加))
sem加1
如sem<=0,唤醒一个等待进程
特性
- 信号量是被保护的整数变量
初始化完成后,只能通过P()和V()操作修改
由操作系统保证,PV操作是原子操作
- P()可能阻塞,V()不会阻塞
- 通常假定信号量是“公平的”
线程不会被无限期阻塞在P()操作
假定信号量等待按先进先出排队
自旋锁是做不到先进先出的,因为自旋锁需要占用CPU随时地去查,有可能临界区使用者退出的时候它刚查完,下一个进入者是谁去查它就能进去
分类
- 信号量:资源数目为0或1
- 资源信号量:资源数目为任何非负值
二者之间的关系:等价,基于一个可以实现另一个
使用
- 互斥访问 :临界区的互斥访问控制
- 条件同步 :线程间的事件等待
信号量实现同步互斥.
- 每类资源设置一个信号量,其初值为1
- 必须成对使用P()和V()操作
P()操作保证互斥访问临界资源
V()操作在使用后释放临界资源
PV操作不能次序错误、重复或遗漏
信号量实现条件同步
- 条件同步设置一个信号量,其初值为0
- 条件等待:线程A要等待线程B执行完X模块才能执行N模块
假设B先执行完X,此时信号量变成1,则N能直接往下执行
假设A先执行完M,此时信号量变成-1,阻塞,进入等待状态,然后进程B进行V操作,释放,此时信号量为0。若信号量小于或等于0,则说明有线程在等待,此时唤醒等待中的线程A,执行N模块
使用瓶颈
- 读/开发代码比较困难
程序员需要能运用信号量机制
- 容易出错
使用的信号量已经被另一个线程占用
忘记释放信号量
- 不能够处理死锁问题
管程
组成
- 一个锁 + 0个或多个条件变量
- 锁 = 控制管程代码的互斥访问
- 0个或多个条件变量 = 0个等同于临界区,管理共享数据的并发访问
定义
管程是一种用于多线程互斥访问共享资源的程序结构 , 采用面向对象方法,简化了线程的同步机制, 任一时刻最多只有一个线程执行管理代码,正在管程中的线程可临时放弃管理的互斥访问,等待事件出现时恢复
条件变量(Condition Variable)
条件变量是管程内的等待机制
进入管程的线程因资源被占用而进入等待状态
每个条件变量表示一种等待原因,对应一个等待队列
- Wait()操作
将自己阻塞在等待队列中
唤醒一个等待着或释放管程的互斥访问
- Signal()操作
将等待队列中的一个线程唤醒
如果等待队列为空,则等同空操作
Hansen 管程和Hoare 管程
Hansen 管程
- 主要用于真实操作系统和Java中
- 当前正在执行的线程优先级更高
- 效率更高,少了一次切换
Hoare 管程
- 主要见于教材
- 内部的线程优先执行
- 优先角度考虑更合理,更容易证明正确性