Source: <mordern operating system> by Andrew S. Tanenbaum
In 1965, Dijkstra posed and solved a synchronization problem he calledDining Philosophers Problem. Since that time, everyone inventing yet another synchronization primitive has felt obligated to demonstrate how wonderful the new primitive is by showing how should it solves the Dining Philosophers problem. the problem can be stated quite simply as follows. five philosophers are seated around a circular table. each philosopher has a plate of spaghetti. the spaghetti is so slippery that a philosopher needs two forks to eat it. between each pair of plates is one fork. the layout of the table is already strated in Fig. 2-31.
(Google is not posted)
Fig.2-31
The life of a philosopher consists of alternate periods of eating and thinking. (This is something of an authorization action, even for philosophers, but the other activities are irrelevant here .) when a philosopher gets hungry, she tries to acquire her left and right fork, one at a time, in either order. if successful in acquiring two forks, she eats for a while, then puts down the forks, and continues Think. The key question is: can you write a program for each philosopher that does what it is supposed to do and never gets stuck? (It has been pointed out that the two-fork requirement is somewhat artificial; perhaps we shoshould switch from Italian food to Chinese food, substituting rice for spaghetti and chopsticks for forks .)
Figure 2-32 shows the obvious solution. The procedureTake_forkWaits until the specified fork is available and then seizes it. unfortunately, the obvious solution is wrong. suppose that all five philosophers take their left forks simultaneously. none will be able to take their right forks, and there will be a deadlock.
#define N 5 /* number of philosophers */ void philosopher(int i) /* i: philosopher number, from 0 to 4 */{ while (TRUE) { think( ); /* philosopher is thinking */ take_fork(i); /* take left fork */ take_fork((i+1) % N); /* take right fork; % is modulo operator */ eat(); /* yum-yum, spaghetti */ put_fork(i); /* Put left fork back on the table */ put_fork((i+1) % N); /* put right fork back on the table */ }}
Figure 2-32.A nonsolution to the Dining Philosophers problem.
We cocould modify the program so that after taking the left fork, the program checks to see if the right fork is available. if it is not, the philosopher puts down the left one, waits for some time, and then repeats the whole process. this proposal too, fails, although for a different reason. with a little bit of bad luck, all the philosophers cocould start the Algorithm Simultaneously, picking up their left forks, seeing that their right forks were not available, putting down their left forks, waiting, picking up their left forks again simultaneously, and so on, forever. A situation like this, in which all the programs continue to run indefinitely but fail to make any progress is calledStarvation. (It is called starvation even when the problem does not occur in an Italian or a Chinese restaurant .)
Now you might think, "If the philosophers wocould just wait a random time instead of the same time after failing to acquire the right-hand fork, the chance that everything wowould continue in lockstep for even an hour is very small. "this observation is true, and in nearly all applications trying again later is not a problem. for example, in the popular Ethernet local area network, if two computers send a packet at the same time, each one waits a random time and tries again; in practice this solution works fine. however, in a few applications one wowould prefer a solution that always works and cannot fail due to an unlikely series of random numbers. think about safety control in a nuclear power plant.
One improvement to Fig. 2-32 that has no deadlock and no starvation is to protect the five statements following the callThinkBy a binary semaphore. Before starting to acquire forks, a philosopher wocould do a down onMutex.After replacing the forks, she wocould do an up onMutex. From a theoretical viewpoint, this solution is adequate. from a practical one, it has a performance bug: Only one philosopher can be eating at any instant. with five forks available, we shocould be able to allow two philosophers to eat the same time.
The solution presented in Fig. 2-33 is deadlock-free and allows the maximum parallelism for an arbitrary number of philosophers. It uses an array,State, To keep track of whether a philosopher is eating, thinking, or hungry (trying to acquire forks). A philosopher may move only into eating state if neither neighbor is eating. philosopherI'S neighbors are defined by the macrosLeftAndRicht. In other words, ifIIs 2,LeftIs 1 andRightIs 3.
The program uses an array of semaphores, one per philosopher, so hungry philosophers can block if the needed forks are busy. Note that each process runs the procedurePhilosopherAs its main code, but the other procedures,Take_forks,Put_forks,AndTestAre ordinary procedures and not separate processes.
#define N 5 /* number of philosophers */#define LEFT (i+N−1)%N /* number of i's left neighbor */#define RIGHT (i+1)%N /* number of i's right neighbor */#define THINKING 0 /* philosopher is thinking */#define HUNGRY 1 /* philosopher is trying to get forks */#define EATING 2 /* philosopher is eating */typedef int semaphore; /* semaphores are a special kind of int */int state[N]; /* array to keep track of everyone's state */semaphore mutex = 1; /* mutual exclusion for critical regions */semaphore s[N]; /* one semaphore per philosopher */ void philosopher (int i) /* i: philosopher number, from 0 to N−1 */{ while (TRUE) { /* repeat forever */ think(); /* philosopher is thinking */ take_forks(i); /* acquire two forks or block */ eat(); /* yum-yum, spaghetti */ put_forks(i); /* put both forks back on table */ }} void take_forks(int i) /* i: philosopher number, from 0 to N−1 */{ down(&mutex); /* enter critical region */ state[i] = HUNGRY; /* record fact that philosopher i is hungry */ test(i); /* try to acquire 2 forks */ up(&mutex); /* exit critical region */ down(&s[i]); /* block if forks were not acquired */} void put_forks(i) /* i: philosopher number, from 0 to N−1 */{ down(&mutex); /* enter critical region */ state[i] = THINKING; /* philosopher has finished eating */ test(LEFT); /* see if left neighbor can now eat */ test(RIGHT); /* see if right neighbor can now eat */ up(&mutex); /* exit critical region */} void test(i) /* i: philosopher number, from 0 to N−1 */{ if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) { state[i] = EATING; up(&s[i]); }}
Figure 2-33. A solution to the dining philosophers problem