目录
4.4 更复杂的情况,二次信号量实现不了,例子如下(生消案例)
6.2.1 最简单的想法(死锁:都拿自己左边的,结果就都饿死了)
6.2.2 改进,增加了对右边叉子的判断(还是不行,五个铁憨憨,一起拿,一起放,还是饿死了)
6.2.3 修改等待时间为随机时间,让他们不会同时放叉子,可行但不够完美(治标不治本)
6.2.4 每次拿叉子,就不允许其它人吃(可行,但只能一个人)
1. 概述
- 背景
- 信号量
- 信号量使用
- 信号量实现
- 管程
- 经典同步问题
1.1 上次课内容的复习
- 并发问题:竞争条件(竞态条件)
- 多程序并发存在大的问题
- 同步
- 多线程共享公共数据的协调执行
- 包括互斥与条件同步
- 互斥:在同一时间只有-个线程可以执行临界区
- 确保同步正确很难?
- 需要高层次的编程抽象(如:锁)
- 从底层硬件支持编译
1.2 结构图
2 信号量(实现同步和互斥)
2.1 概述
信号量:
- 抽象数据类型
- 一个整形(sem), 两个原子操作
- P(): sem减1,如果sem<0, 等待,否则继续
- V(): sem加1, 如果sem<=0,唤醒一个等待的P
2.2 机制的发现
3 信号量的使用
3.1 信号量是什么
- 信号量是整数
- 信号量是被保护的变量
- 初始化完成后,唯一改变一个信号量的值的办法是通过P()和V()
- 操作必须是原子
- P()能够阻塞,V()不会阻塞
- 我们假定信号量是“公平的”,即在挑选唤醒哪一个线程时,是按照FIFO进行的,确保公平性。
- 没有线程被阻塞在P ()仍然堵塞如果V ()被无限频繁调用(在同一个信号量)
- 在实践中,FIF0经常被使用
4 信号量的具体实现
4.1 概述
- 可以通过两种类型信号量实现
- 二进制信号量:可以是0或1
- 一般/计数信号量:可取任何非负值
- 两者相互表现(给定一个可以实现另-一个)
- 信号量可以用在2个方面
- 互斥
- 条件同步(调度约束一--个线程等待另一个线程的事情发生)
4.2 使用的例子,互斥
4.3 用信号量实现同步
4.4 更复杂的情况,二次信号量实现不了,例子如下(生消案例)
- 一个正确的方法,需要满足的要求:
- 在任何一个时间只能有一个线程操作缓冲区(互斥)
- 当缓冲区为空,消费者必须等待生产者(调度/同步约束)
- 当缓存区满,生产者必须等待消费者(调度/同步约束)
4.4.1 解决:每个约束用一个单独的信号量
- 每个约束用一个单独的信号量
- 二进制信号量互斥
- 一般信号量fullBuffers
- 一般信号量emptyBuffers
4.5 消费者案例的实现
4.6 p和v底层是怎末实现的
4.7 需要注意的问题
- 信号量的双用途
- 互斥和条件同步
- 但等待条件是独立的互斥
- 读/开发代码比较困难
- 程序员必须非常精通信号量
- 容易出错
- 使用的信号量已经被另一个线程占用
- 忘记释放信号量
- 不能够处理死锁问题
5 管程(抽象程度更高于信号量)
5.1 概述
- 提出时,是应用于语言的。
- 目的:分离互斥和条件同步的关注
- 什么是管程
- 一个锁:指定临界区
- 0或者多个条件变量:等待/通知信号量用于管理并发访问共享数据
- 一般方法
- 收集在对象/模块中的相关共享数据
- 定义方法来访问共享数据
5.1.1 示意图
- Lock
- Lock::Acquire() ——等待直到锁可用,然后抢占锁
- Lock::Release() ——释放锁,唤醒等待者如果有
- Condition Variable
- 允许等待状态进入临界区
- 允许处于等待(睡眠)的线程进入临界区
- 某个时刻原子释放锁进入睡眠
- ➢Wait() operation
- 释放锁,睡眠,重新获得锁返回后
- Signal() operation (or broadcast() operation)
- 唤醒等待者(或者所有等待者),如果有
5.2 条件变量的实现
5.3 用管程解决消费者案例
5.4 signal操作的说明
5.5 总结
6 两个问题
6.1 读-写问题
6.1.1 读者优先,如果有读者在读,写者要先等待
6.1.2 读者优先的实现
6.1.3 读者优先:建立管程大概的表现形式
6.1.4 根据上述的伪代码 实现代码
6.1.5 写者优先
6.2 哲学家就餐
6.2.1 最简单的想法(死锁:都拿自己左边的,结果就都饿死了)
6.2.2 改进,增加了对右边叉子的判断(还是不行,五个铁憨憨,一起拿,一起放,还是饿死了)
6.2.3 修改等待时间为随机时间,让他们不会同时放叉子,可行但不够完美(治标不治本)
6.2.4 每次拿叉子,就不允许其它人吃(可行,但只能一个人)
6.2.5 问题
6.2.6 解决思路1
6.2.7 解决思路2--计算机版
6.2.8 解决思路3--程度
6.2.9 哲学家函数表示
函数take_forks
test_ take_ left_ right_ forks函数
put_forks