文章目录
1. 死锁的概念
1.1 哲学家就餐问题
问题描述:五个哲学家围坐在圆桌边,有5支筷子。他们做的事就是:思考-休息-吃饭
吃饭时使用两支筷子:
- 拿一双筷子才能吃饭
- 每次只能取一支筷子
- 只取身边的筷子
- 吃完放下筷子
用线程实现哲学家的生活,如下伪代码所示:
int signal[5] = [1, 1, 1, 1, 1]; //筷子i是否可用:0=不可用;1=可用
Philosopher (int i)
{
while(TRUE){
思考;
休息;
P(signal[i]); //取左边的筷子;
P(signal[(i+4)%5]); //取右边的筷子;
吃饭;
V(signal[(i+4)%5]); //放下右边的筷子;
V(signal[i]); //放下左边的筷子;
放下右边的筷子;
放下左边的筷子;
}
}
假如5个哲学家在同一时刻吃饭,大家都拿起了左边的筷子,但是右边的筷子没有,所有人都在等待右边人放下手中的筷子。而右边的人没有吃完之前又不会放下筷子,所以所有的人都缺一支筷子永远无法吃完饭,这时候就陷入了死锁
。
1.2 死锁定义
两个或多个进程无限期地等待永远不会发生的条件的一种系统状态。其结果就是每个进程都永远阻塞。
另一种定义:在两个或多个进程中,每个进程都持有某种资源,但又继续申请其他的进程已持有的资源,此时每个进程都拥有其运行所需的一部分资源,但是又都不够,从而每个进程都无法向前推进,限于阻塞状态。这种状态称为死锁。
2. 死锁的起因
- 系统资源有限:资源的数目不足,进程对资源竞争导致死锁
- 并发程序的推进顺序不当:进程请求资源和释放资源的顺序不当,导致死锁
- 不正当的P-V操作也可能会带来死锁
2.1 死锁的一些结论
- 参与死锁的进程至少是两个
- 参与死锁的进程至少有两个已经占有资源
- 参与死锁的所有进程都在等待资源
- 参与死锁的进程是当前系统中所有进程的子集
- 死锁会浪费大量的系统资源,甚至导致系统崩溃
3. 死锁的预防策略
3.1 死锁的必要条件
条件 | 描述 |
---|---|
互斥条件 | 进程互斥使用资源,资源具有独占性 |
不剥夺条件 | 进程在访问完资源前不能被其他进程强行剥夺 |
部分分配条件 | 进程边运行边申请资源,临时需要临时分配 |
环路条件 | 多个进程构成环路:环中每个进程已占用的资源前一进程所示,而自己所申请的资源又被环中后已进程占用着 |
上面讨论的哲学家吃饭问题,如果限定最多只有4人同时吃饭,就可以避免死锁。因为此时环路条件被破坏了。
3.2 解决死锁策略
通过设置某些限制条件,破坏死锁的四个必要条件中的一个或多个来防止死锁.
- 破坏互斥条件(难)
- 破坏不剥夺条件(代价大)
- 破坏部分分配条件(预先静态分配)
- 破坏环路条件(有序资源分配)
3.2.1 预先静态分配法
进程运行前将所需资源一次性全部分配给它。因此进程在运行的过程中不再提出资源请求,从而避免出现阻塞或死锁。
特点:
- 执行可能被延迟
- 应用开销大:运算前要估算资源请求
- 资源利用率低:资源被占用而不用
改进:
- 资源分配的单位油由进程改为程序步
3.2.2 有序资源分配法
系统中的每个资源有一个唯一序号,进程每次申请资源时只能申请序号更大的资源。如果进程已占有资源的信号最大为M,则下次申请只能申请大于M的资源,而不能申请序号小于M的资源。
实行过程:
- 分配资源时检查是否符合递增规定
- 若不符合,在拒绝申请,并撤销该进程
- 若符合,且资源可用则予以分配。若资源不可能则不分配,陷于阻塞
3.3 Windows和Linux采取的措施
鸵鸟策略
- 不管死锁,选择相信开发人员。不采取任何措施。