99 lines
No EOL
2.9 KiB
Markdown
99 lines
No EOL
2.9 KiB
Markdown
The first real practical example of a concurrent system.
|
||
- $N$ philosophers seated around a circular table
|
||
- one chopstick between each pair of philosophers
|
||
- a philosophers must pick up its two nearest chopsticks in order to eat
|
||
- a philosopher must pick up first one chopstick, then the second one, not both at once
|
||

|
||
|
||
**PROBLEM:** *Devise a deadlock-free algorithm for allocating these limited resources (chopsticks) among several processes (philosophers).*
|
||
|
||
#### A wrong solution
|
||
each chopstick is governed by a mutual exclusion semaphore that prevents any other philosopher from picking up the chopstick when it is already in use by another philosopher
|
||
|
||
```
|
||
semaphore chopstick[5] initialized to 1
|
||
Philosopher(i) :=
|
||
while(1) do
|
||
chopstick[i].down()
|
||
chopstick[(i+1)%N].down()
|
||
// eat
|
||
chopstick[(i+1)%N].up()
|
||
chopstick[i].up()
|
||
```
|
||
No two neighbours can eat simultaneously, but we can have a deadlock if all philosophers grab their right chopstick simultaneously.
|
||
|
||
#### Solution 1
|
||
Give a number to all forks and always try with the smaller.
|
||
All philosophers must first pick left and then right, except for the last one that first picks right and then left.
|
||
|
||
So there will be a contention on one fork, so this way a process is automatically excluded.
|
||
|
||
```
|
||
semaphores fork[N] all initialized at 1;
|
||
Philosopher(i) :=
|
||
Repeat
|
||
think;
|
||
if (i < N-1) then
|
||
fork[i].down();
|
||
fork[i+1].down();
|
||
else
|
||
fork[0].down();
|
||
fork[N-1].down();
|
||
eat;
|
||
fork[(i+1)%N].up();
|
||
fork[i].up();
|
||
```
|
||
This is deadlock free. But not much efficient as it may happen that only a philosopher per time will have both the forks (but that is another problem).
|
||
|
||
#### Solution 3
|
||
Allow at most N-1 philosophers at a time sitting at the table
|
||
|
||
```
|
||
semaphores fork[N] all initialized at 1
|
||
semaphore table initialized at N-1
|
||
|
||
Philosopher(i) :=
|
||
Repeat
|
||
think;
|
||
table.down();
|
||
fork[i].down();
|
||
fork[(i+1)%N].down();
|
||
eat;
|
||
fork[(i+1)%N].up();
|
||
fork[i].up();
|
||
table.up()
|
||
```
|
||
In this case we break the symmetry by letting an odd number of philosophers sit at the table at most.
|
||
|
||
#### Solution 4 - Monitors!
|
||
Pick up 2 chopsticks only if both are free
|
||
- a philosopher moves to his/her eating state only if both neighbors are not in their eating states
|
||
- need to define a state for each philosopher
|
||
- if one of my neighbors is eating, and I’m hungry, ask them to signal me when they’re done
|
||
- thus, states of each philosopher are: thinking, hungry, eating
|
||
- need condition variables to signal waiting hungry philosopher(s)
|
||
|
||
```
|
||
monitor DP
|
||
status state[N] all initialized at thinking;
|
||
condition self[N];
|
||
|
||
Pickup(i) :=
|
||
state[i] = hungry;
|
||
test(i);
|
||
if (state[i] != eating) then
|
||
self[i].wait;
|
||
|
||
Putdown(i) :=
|
||
state[i] = thinking;
|
||
test((i+1)%N);
|
||
test((i-1)%N);
|
||
|
||
test(i) :=
|
||
if (state[(i+1)%N] != eating
|
||
&& state[(i-1)%N] != eating
|
||
&& state[i] == hungry) then
|
||
state[i] = eating;
|
||
self[i].signal();
|
||
```
|
||
N.B.: pickup picks both forks simultaneously |