3.7 KiB
19/10/20
The Dining Philosophers Problem
The problem is defined as:
- Five philosophers are sitting on a round table
- Each one has a plate of spaghetti
- The spaghetti is too slippery, and each philosopher needs 2 forks to be able to eat
- When hungry, the philosopher tries to acquire the forks on his left and right.
Note that this reflects the general problem of sharing a limited set of resources (forks) between a number of processes (philosophers).
Solution 1
Forks are represented by semaphores (initialised to 1)
- 1 if the fork is available: the philosopher can continue.
- 0 if the fork is unavailable: the philosopher goes to sleep if trying to acquire it.
Solution: Every philosopher picks up one fork and waits for the second fork to become available (without putting the first one down).
This solution will deadlock every time.
- The deadlock can be avoided by exponential decay. This is where a philosopher puts down their fork and waits for a random amount of time. (this is how Ethernet systems avoid data collisions)
- Just add another fork
Solution 2
One global mutex set by a philosopher when they want to eat (only one can eat at a time)
Question: Can I initialise the value of the eating semaphore to 2 to create more parallelism?
Setting the semaphore to 2 allows the possibility of 2 philosophers to eat at one time. If these two philosophers are sitting next to each other then they will try to grab the same fork. The code will not deadlock, however only one (sometimes two) philosopher(s) is able to eat.
Solution 3
A more sophisticated solution is necessary to allow maximum parallelism
The solution uses:
state[N]: one state variable for every philosopher (THINKINGHUNGRYandEATING)phil[N]: one semaphore per philosopher (i.e. not forks initialised to 0)
- The philosopher goes to sleep if one of their neighbours are eating
- The neighbours wake up the philosopher if they have finished eating
sync: one semaphore/mutex to enforce mutual exclusion of the critical section (while updating the states ofhungrythinkingandeating)- A philosopher can only start eating if their neighbours are not eating.
Code for Solution 3
#define N 5
#define THINKING 1
#define HUNGRY 2
#define EATING 3
int state[N] = {THINKING, THINKING, THINKING, THINKING, THINKING};
sem_t phil[N]; // sends philosopher to sleep
sem_t sync;
void * philosopher(void * id) {
int i = *((int *) id);
while(1) {
printf("%d is thinking\n", i);
take_forks(i);
printf("%d is eating\n", i);
put_forks(i);
}
}
void take_forks(int i) {
sem_wait(&sync);
state[i] = HUNGRY;
test(i); //checks surrounding philosophers to see if its ok to eat
sem_post(&sync);
sem_wait(&phil[i]); //1 -> 0
}
void test(int i) {
int left = (i + N - 1) % N;
int right = (i + 1) % N;
if(state[i] == HUNGRY && state[left] != EATING && state[right] != EATING) {
state[i] = EATING;
sem_post(&phil[i]); //0 -> 1
}
}
void put_forks(int i) {
int left = (i + N - 1) % N;
int right = (i + 1) % N;
sem_wait(&sync);
state[i] = THINKING;
test(left);
test(right);
sem_post(&sync);
}
void test(int i) {
int left = (i + N - 1) % N;
int right = (i + 1) % N;
if(state[i] == HUNGRY && state[left] != EATING && state[right] != EATING) {
state[i] = EATING;
sem_post(&phil[i]);
}
}
