一、信号量
1. 信号量是操作系统提供的一种协调共享资源访问的方法,用信号量表示系统资源的数量。
信号量是一种抽象数据类型,由一个整形(sem)变量和两个原子操作组成。
2. 这两个原子操作分别是
- P()(Prllaag:荷兰语,尝试减少)
- sem减一
- 若sem<0,进入等待,否则继续
- V()(Verhoog:荷兰语,增加)
- sem加一
- 若sem≤0,唤醒一个等待进程
3. 信号量的特性
- 信号量是被保护的整数变量
- 初始化完成后,只能通过P() 和V()操作修改
- 由操作系统保证,PV操作是原子操作
- P() 可能阻塞,V()不会阻塞
- 通常假定信号量是“公平的”
- 线程不会无限期的阻塞在P() 操作
- 假定信号量等待按FSFO(先进先出)排队
4. 信号量的实现
二、信号量的使用
1. 信号量分类,可分为两种信号量
- 二进制信号量:资源数目为0或1
- 资源信号量:资源数目为任何非负值
两者等价,基于一个可以实现另一个。
2.用信号量实现临界区的互斥访问
每个临界区设置一个信号量,其初值为1。
必须成对使用P()操作和V()操作
- P()操作保证互斥访问临界资源
- V()操作在使用后释放临界资源
- PV操作不能次序错误,重复或遗漏
3.用信号量实现条件同步
同步:合作的并发进程需要按先后次序执行。例如:一个进程的执行依赖于合作进程的消息或信号。当一个进程没有得到来自合作进程的消息或信号时需阻塞等待,直到消息或信号到达才唤醒。
4. 消费者-生产者问题
有界缓冲区的生产者-消费者问题描述
- 一个或多个生产者在生成数据后放在一个缓冲区里
- 单个消费者从缓冲区取出数据处理
- 任何时刻只能有一个生产者或者消费者可访问缓冲区
5. 用信号量解决生产者-消费者问题
问题分析:
- 任何时候时刻只能有一个线程操作缓冲区(互斥访问)
- 缓冲区空时,消费者必须等待生产者(条件同步)
- 缓冲区满时,生产者必须等待消费者(条件同步)
用信号量描述每个约束:
- 二进制信号量mutex
- 资源信号量fullBuffers
- 资源信号量emptyBuffers
实现过程:
- 信号量赋值:mutex赋值为1,fullBuffers赋值为0,emptyBuffers赋值为n。
- Deposit线程:对emptyBuffers进行P操作,emptyBuffers减一,当emptyBuffers为0时,表示缓冲区满,作P操作将发生阻塞,需等待Remove线程中对emptyBuffers进行V操作,表示消费者从缓冲区移出了一次数据。从而完成了条件同步
- 对mutex进行P操作,mutex变为0,若此时切换到Remove线程,mutex再次执行P操作会发生堵塞,从而完成互斥的功能。
- 将数据c添加到缓冲区,并对mutex进行V操作,mutex加一,相当于解除锁定。
- fullBuffers进行V操作,fullBuffers加一,可表示生产者已经添加了一次数据到缓冲区。
- Remove线程:对fullBuffers进行P操作,若fullBuffers非零,则其值减一后继续往下执行。若fullBuffers的值为0,表示缓冲区没有数据,则对其进行P操作将引起阻塞,需等待Deposit线程中的V操作。从而完成了条件同步。
- 同Desposit线程,在将数据移出缓冲区的操作之前和之后分别对mutex进行P操作和V操作,可完成互斥功能。
- 对emptyBuffers进行V操作,emptyBuffers加一,表示消费者已经从缓冲区移出了一次数据。
问1:Deposit线程中emptyBuffers的P操作与mutex的P操作能否互换位置?
答:不能,因为P操作可能引起阻塞,如:当emptyBuffers为0时表示缓冲区满,此时先执行mutex的P操作,可继续往下执行,再对emptyBuffers执行P操作时,会被阻塞,到消费者线程,fullBufffers进行P操作,可继续往下执行,再对mutex执行P操作,此时mutex为0,会产生阻塞,从而不能继续往下执行到emptyBuffers的V操作,从而产生了死锁。
问2:Deposit线程中mutex的V操作与fullBuffers的P操作能否互换位置?
答:能,因为V操作的作用是使得信号量加一,并且唤醒等待的线程,因此不会产生阻塞,可以互换位置。