
先来看看哲学家就餐的问题描述:
- 5 个⽼⼤哥哲学家,闲着没事做,围绕着⼀张圆桌吃⾯;
- 巧就巧在,这个桌⼦只有 5 ⽀叉⼦,每两个哲学家之间放⼀⽀叉⼦;
- 哲学家围在⼀起先思考,思考中途饿了就会想进餐;
- 奇葩的是,这些哲学家要两⽀叉⼦才愿意吃⾯,也就是需要拿到左右两边的叉⼦才进餐;
- 吃完后,会把两⽀叉⼦放回原处,继续思考;
那么问题来了,如何保证哲 学家们的动作有序进⾏,⽽不会出现有⼈永远拿不到叉⼦呢?
1、方案一:信号量
⽤信号量的⽅式,也就是 PV 操作来尝试解决它,代码如下:

上⾯的程序,好似很⾃然。拿起叉⼦⽤ P 操作,代表有叉⼦就直接⽤,没有叉⼦时就等待其他哲学家放回叉⼦。

不过,这种解法存在⼀个极端的问题:假设五位哲学家同时拿起左边的叉⼦,桌⾯上就没有叉⼦了,这样就没有⼈能够拿到他们右边的叉⼦,也就说每⼀位哲学家都会在 P(fork[(i + 1) % N ]) 这条语句阻塞了,很明显这发⽣了死锁的现象。
2、方案二:使用互斥信号量
既然「⽅案⼀」会发⽣同时竞争左边叉⼦导致死锁的现象,那么我们就在拿叉⼦前,加个互斥信号量,代码如下:

上⾯程序中的互斥信号量的作⽤就在于,只要有⼀个哲学家进⼊了「临界区」,也就是准备要拿叉⼦时,其他哲学家都不能动,只有这位哲学家⽤完叉⼦了,才能轮到下⼀个哲学家进餐。

⽅案⼆虽然能让哲学家们按顺序吃饭,但是每次进餐只能有⼀位哲学家,⽽桌⾯上是有 5 把叉⼦,按道理是能可以有两个哲学家同时进餐的,所以从效率⻆度上,这不是最好的解决⽅案。
3、方案三:采用分支结构
那既然⽅案⼆使⽤互斥信号量,会导致只能允许⼀个哲学家就餐,那么我们就不⽤它。
另外,⽅案⼀的问题在于,会出现所有哲学家同时拿左边⼑叉的可能性,那我们就避免哲学家可以同时拿左边的⼑叉,采⽤分⽀结构,根据哲学家的编号的不同,⽽采取不同的动作。
即让偶数编号的哲学家「先拿左边的叉⼦后拿右边的叉⼦」,奇数编号的哲学家「先拿右边的叉⼦后拿左边的叉⼦」。

上⾯的程序,在 P 操作时,根据哲学家的编号不同,拿起左右两边叉⼦的顺序不同。另外,V 操作是不需要分⽀的,因为 V 操作是不会阻塞的。

⽅案三即不会出现死锁,也可以两⼈同时进餐。
4、方案四:使用state数组记录哲学家状态
这里提出另外⼀种可⾏的解决⽅案,我们⽤⼀个数组 state 来记录每⼀位哲学家在进程、思考还是饥饿状态(正在试图拿叉⼦)。
那么,⼀个哲学家只有在两个邻居都没有进餐时,才可以进⼊进餐状态。
第 i 个哲学家的左邻右舍,则由宏 LEFT 和 RIGHT 定义:
- LEFT : ( i + 5 - 1 ) % 5
- RIGHT : ( i + 1 ) % 5
⽐如 i 为 2,则 LEFT 为 1, RIGHT 为 3。
具体代码实现如下:


上⾯的程序使⽤了⼀个信号量数组,每个信号量对应⼀位哲学家,这样在所需的叉⼦被占⽤时,想进餐的哲学家就被阻塞。
注意,每个进程/线程将 smart_person 函数作为主代码运⾏,⽽其他 take_forks 、 put_forks 和test 只是普通的函数,⽽⾮单独的进程/线程。


⽅案四同样不会出现死锁,也可以两⼈同时进餐。
学自小林coding所著的《图解系统》,侵删

4494

被折叠的 条评论
为什么被折叠?



