2.9 KiB
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 !/Concurrent Systems/notes/images/Pasted image 20250317100456.png
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