📜  餐饮哲学家问题

📅  最后修改于: 2020-12-15 09:32:10             🧑  作者: Mango

用餐哲学问题

餐饮哲学家的问题是经典的同步问题,即五个哲学家围坐在一张圆桌旁,他们的工作是交替思考和吃饭。一碗面条放在桌子中央,每个哲学家用五根筷子。要吃一个哲学家,既需要左右筷子。只有当哲学家的左,右筷子都可用时,他才能进食。如果哲学家的左,右筷子都不可用,则哲学家放下他们的(左或右)筷子,然后重新开始思考。

餐饮哲学家展示了大量并发控制问题,因此这是一个经典的同步问题。

围坐在桌子旁的五位哲学家

餐饮哲学家问题-让我们用以下代码了解餐饮哲学家问题,我们使用图1作为参考,以使您准确地理解问题。五个哲学家分别表示为P0,P1,P2,P3和P4,五个筷子分别表示为C0,C1,C2,C3和C4。

Void Philosopher
 {
 while(1)
  {
   take_chopstick[i];
   take_chopstick[ (i+1) % 5] ;
   . .
   . EATING THE NOODLE
   .
   put_chopstick[i] );
   put_chopstick[ (i+1) % 5] ;
   .
   . THINKING
  }
}

让我们讨论上面的代码:

假设哲学家P0要吃饭,它将进入Philosopher()函数,并执行take_chopstick [i];通过执行此操作,在执行take_chopstick [(i + 1)%5]之后,它将保持C0筷子通过这样做,它握住了C1筷子(因为i = 0,因此(0 + 1)%5 = 1)

同样,假设现在哲学家P1要吃东西,它将进入Philosopher()函数,并执行take_chopstick [i];通过执行此操作,在执行take_chopstick [(i + 1)%5]之后,它会握住C1筷子通过这样做,它握住了C2筷子(因为i = 1,因此(1 + 1)%5 = 2)

但是实际上筷子C1不可用,因为它已经被哲学家P0所采用,因此上述代码会产生问题并产生竞争条件。

餐饮哲学家问题的解决方案

我们使用信号量来代表筷子,这确实可以解决餐饮哲学家问题。将使用“等待”和“信号”操作来解决“用餐哲学家”问题,可以执行摘筷子的等待操作,而可以释放筷子的信号量。

信号量:信号量是S中的一个整数变量,除初始化外,信号量仅可通过两个标准原子操作-wait和signal访问,其定义如下:

1. wait( S )
{
while( S <= 0) ;
S--;
}

2. signal( S )
{
S++;
}

从以上wait的定义中可以清楚地看出,如果S <= 0,则它将进入无限循环(由于分号;在while循环之后)。信号的工作是增加S的值。

筷子的结构是一个信号量数组,如下所示:

semaphore C[5];

最初,信号量C0,C1,C2,C3和C4的每个元素都将初始化为1,因为筷子在桌子上,任何哲学家都没有捡起。

让我们通过使用信号量操作wait和signal来修改Dining哲学家问题的上述代码,所需的代码如下所示:

void Philosopher
 {
 while(1)
  {
   Wait( take_chopstickC[i] );
   Wait( take_chopstickC[(i+1) % 5] ) ;
   . .
   . EATING THE NOODLE
   .
   Signal( put_chopstickC[i] );
   Signal( put_chopstickC[ (i+1) % 5] ) ;
   .
   . THINKING
  }
}

在上面的代码中,对take_chopstickC [i]和take_chopstickC [(i + 1)%5]执行第一次等待操作。这表明哲学家我从左和右拿起了筷子。然后执行进食函数。

一旦哲学家i进食完成,就对take_chopstickC [i]和take_chopstickC [(i + 1)%5]进行信号操作。这表明我吃过哲学家并放下了左右筷子。最后,哲学家开始重新思考。

让我们了解一下上面的代码如何解决用餐哲学家的问题?

令i = 0(初始值),假设哲学家P0要吃东西,它将进入Philosopher()函数,并执行Wait(take_chopstickC [i]);通过这样做,它将保持C0筷子并将信号量C0减小为0 然后执行Wait(take_chopstickC [(i + 1)%5]);通过这样做,它握住了C1筷子(因为i = 0,因此(0 + 1)%5 = 1)并将信号量C1减小为0

同样,假设现在哲学家P1要吃饭,它将进入Philosopher()函数,并执行Wait(take_chopstickC [i]);通过这样做,它将尝试握住C1筷子,但将无法执行此操作因为信号器C1的值已被哲学家P0设置为0,因此它将进入无限循环,因此哲学家P1不会这样做能够拿起筷子C1,而如果哲学家P2想吃饭,它将进入Philosopher()函数,并执行Wait(take_chopstickC [i]);通过这样做,它握住了C2筷子并将信号量C2减小为0,之后,它执行Wait(take_chopstickC [(i + 1)%5]);通过这样做,它将握住C3筷子(因为i = 2,因此(2 + 1)%5 = 3)并将信号量C3减小为0。

因此,以上代码为餐饮哲学家的问题提供了解决方案,只有当哲学家的左,右筷子都可用时,哲学家才能进餐,而哲学家则需要等待。同样,两个独立的哲学家可以同时进食(即,哲学家P0和P2,P1和P3以及P2和P4可以同时进食,因为它们都是独立的过程,并且遵循上述用餐哲学家问题的约束)

用餐哲学家问题的上述解决方案的缺点

通过上述用餐哲学家问题的解决方案,我们证明了没有两个相邻的哲学家可以在同一时间进餐。上述解决方案的缺点是该解决方案可能导致死锁状态。如果所有哲学家都同时拿起左筷子,就会发生这种情况,这将导致僵局,并且所有哲学家都无法进食。

为了避免死锁,一些解决方案如下:

  • 桌子上最多可容纳四名哲学家,在这种情况下,筷子C4可用于哲学家P3,因此P3将开始进食,并且在进食过程结束后,他将两根筷子放下和C4(即信号量C3和C4)现在将增加到1。现在,持有筷子C2的哲学家P2也将有筷子C3,因此类似地,他将在进食后放下筷子,并允许其他哲学家进食。
  • 处于偶数位置的哲学家应先选择右筷子,然后再选择左筷子,而处于奇数位置的哲学家应先选择左筷子,然后再选择右筷子。
  • 只有在两个筷子(左和右)同时可用的情况下,才应允许哲学家捡起他们的筷子
  • 所有四个开始的哲学家(P0,P1,P2和P3)都应先选择左筷子,然后再选择右筷子,而最后一个哲学家P4应先选择右筷子,然后再选择左筷子。这将迫使P4首先握住他的右筷子,因为P4的右筷子是C0,已经被哲学家P0握住,并且其值已设置为0,即C0已经为0,因此P4将陷入无限环和筷子C4仍然空着。因此,哲学家P3可以同时使用左C3和右C4筷子,因此它将开始进食并在完成后放下两根筷子,让其他人进食,从而消除了死锁的问题。

问题的设计是为了说明避免死锁的挑战,系统的死锁状态是无法进行系统升级的状态。考虑一个指导每个哲学家的行为如下的提议:

  • 指导哲学家思考直到左叉可用为止,如果可用,请握住它。
  • 指导哲学家思考,直到合适的叉子可用时,握住它。
  • 当两个叉子都可用时,指示哲学家进餐。
  • 然后,先放下右叉
  • 然后放下左叉
  • 从头开始重复。