The Dining Philosopher Problem is a classic synchronization problem introduced by Edsger Dijkstra in 1965. It illustrates the challenges of resource sharing in concurrent programming, such as deadlock, starvation, and mutual exclusion.
Each philosopher alternates between thinking and eating.
There is one chopstick between each philosopher (total K chopsticks).
A philosopher must pick up two chopsticks (left and right) to eat.
Only one philosopher can use a chopstick at a time.
The challenge: Design a synchronization mechanism so that philosophers can eat without causing deadlock (all waiting forever) or starvation (some never get a chance to eat).
Issues in the Problem
Deadlock: If every philosopher picks up their left chopstick first, no one can pick up the right one circular wait.
Starvation: Some philosophers may never get a chance to eat if others keep eating.
Concurrency Control: Must ensure no two adjacent philosophers eat simultaneously.
Semaphore Solution to Dining Philosopher
We use semaphores to manage chopsticks and avoid deadlock.
Algorithm
Each chopstick is represented as a binary semaphore (mutex).
Philosopher must acquire both left and right semaphores before eating.
After eating, the philosopher releases both semaphores.
Pseudocode
semaphore chopstick[5] = {1,1,1,1,1};
Philosopher(i): while(true) { think(); wait(chopstick[i]); // pick left chopstick wait(chopstick[(i+1)%5]); // pick right chopstick
eat();
signal(chopstick[i]); // put left chopstick signal(chopstick[(i+1)%5]); // put right chopstick
}
Explanation:
semaphore chopstick[5] = {1,1,1,1,1}; Each chopstick is a binary semaphore initialized to 1 (available).
think(); Philosopher spends time thinking.
wait(chopstick[i]); Tries to pick the left chopstick. If itβs free, philosopher takes it; otherwise waits.
wait(chopstick[(i+1)%5]); Tries to pick the right chopstick (using modulo for circular table).
eat(); Philosopher eats once both chopsticks are acquired.
signal(chopstick[i]); and signal(chopstick[(i+1)%5]); Puts down both chopsticks, making them available for neighbors.
Code
Explanation
1. Semaphores for chopsticks
sem_t chopstick[N];
Each chopstick is represented by a binary semaphore (1 = free, 0 = in use).
2. Philosopher function
A philosopher thinks first.
Then tries to pick up left chopstick (sem_wait(chopstick[id])).
Then pick up right chopstick (sem_wait(chopstick[(id+1)%N])).
After acquiring both chopsticks eats.
Finally, puts down both chopsticks (sem_post).
3. Thread creation
Each philosopher is represented as a thread.
pthread_create() starts philosopher actions in parallel.
4. Circular arrangement: Right chopstick is (id+1)%N ensures philosopher 4 gets chopsticks 4 and 0.
Problem with this code
If all philosophers pick up their left chopstick at the same time, they will all wait for the right chopstick deadlock.
Deadlock Avoidance
One common fix is to allow only N-1 philosophers to pick chopsticks at a time. This breaks the circular wait condition.
sem_t room; // new semaphore
sem_init(&room, 0, N-1); // allow only N-1 philosophers inside
// before picking chopsticks sem_wait(&room);
// after eating sem_post(&room);
This ensures that at least one philosopher can always eat, preventing deadlock.