信号量和管程
信号量
抽象数据类型:
- 一个整形(sem),两个原子操作
P:sem减一,如果sem<0则等待,否则继续
V:sem加一,如果sem<=0唤醒一个等待的P
- 信号量是证书
- 信号量是被保护的变量
初始化完成之后,唯一改变一个信号量的值的办法是通过P()和V()
操作必须是原子的
- P()能够阻塞,V()不会阻塞
信号量的使用
- 两种类型信号量
二进制信号量:可以是0或者1
一般/计数信号量:可以取任何非负值
两者互相表现(给定一个可以实现另外一个)
- 信号量可以在2个方面使用
互斥
条件同步
例子:
- 一个线程等待另外一个线程处理事情
比如生产东西和消费东西
互斥是不够的
使用约束
- 正确性要求
在任何一个时间只能由一个线程操作缓冲区
当缓冲区为空,消费者必须等待生产者
当缓冲区满时,生产者必须等待消费者
- 上面对应的每个约束用一个单独的信号量
二进制信号量,互斥
一般信号量
一般信号量
信号量的实现
使用操作系统的等待队列,使得不满足信号量的情况下,睡眠
- 信号量的双用途
互斥和条件同步
等待条件是独立的互斥
- 读、开发代码比较困难
程序员必须非常精通信号量
- 容易出错
使用信号量已经被另一个线程占用
忘记释放信号量
- 不能够处理死锁问题
管程
- 目的:分离互斥和条件同步的关注
- 什么是管程:
一个锁:指定临界区
0或者多个条件变量:等待/通知信号量用于管理并发访问共享数据
- 一般方法
手机在对象/模块中的相关共享数据
定义方法来访问共享数据
经典同步问题
读者-写者问题
- 动机
共享数据访问
- 两种类型使用者
读者:不需要修改数据
写者:读取和修改数据
- 问题约束
允许同一时间有多个读者,但在任何时候只有一个写者
当没有写者时,读者才能访问
当没有读者和写者时,写者才能访问数据
在任何时候只有一个线程可以操作共享变量
- 多个并发线程的数据集共享
读者:只读数据集,他们不执行任何更新
写者:可以读取和写入
- 共享数据
数据集
信号量countmutex初始化为1
信号量writemutex初始化为1
整数rcount初始化为0
基于读者有限策略的方法,只要有一个读者处于活动状态,后来的读者都会被接纳。如果读者源源不断的出现的话,那么写者就会始终处于阻塞状态。
基于写者优先的策略的方法:一旦写者就绪,那么写者会尽可能快的执行写操作,如果写者源源不断的出现的话,那么读者就始终处于阻塞状态。
哲学家就餐问题
5个哲学家围绕一张圆桌而坐,桌子上放着5个叉子,每两个哲学家之间放一个;哲学家的动作包括思考和进餐,进餐时需要同时拿起左边和右边的两个叉子,思考时候需要把叉子放回原处。如何保证哲学家们动作有序进行?
- 必须有一个数据结构描述每个哲学家的状态
- 该状态是一个临界资源,对它的访问应该互斥的进行
- 一个哲学家吃饱之后,可能要唤醒邻居,存在着同步关系