Open In App

Dining Philosopher Problem Using Semaphores

Improve
Improve
Improve
Like Article
Like
Save Article
Save
Share
Report issue
Report

The Dining Philosopher Problem states that K philosophers are seated around a circular table with one chopstick between each pair of philosophers. There is one chopstick between each philosopher. A philosopher may eat if he can pick up the two chopsticks adjacent to him. One chopstick may be picked up by any one of its adjacent followers but not both. 

dining_philosopher_problem

Dining Philosopher

Semaphore Solution to Dining Philosopher

Each philosopher is represented by the following pseudocode:  

process P[i]
 while true do
   {  THINK;
      PICKUP(CHOPSTICK[i], CHOPSTICK[i+1 mod 5]);
      EAT;
      PUTDOWN(CHOPSTICK[i], CHOPSTICK[i+1 mod 5])
   }

There are three states of the philosopher: THINKING, HUNGRY, and EATING. Here there are two semaphores: Mutex and a semaphore array for the philosophers. Mutex is used such that no two philosophers may access the pickup or put it down at the same time. The array is used to control the behavior of each philosopher. But, semaphores can result in deadlock due to programming errors.

Outline of a philosopher process:

Var successful: boolean;
repeat
     successful:= false;
     while (not successful)

     if both forks are available then
     lift the forks one at a time;
     successful:= true;

    if successful = false
    then
    block(Pi);
    {eat}

    put down both forks;

    if left neighbor is waiting for his right fork
    then
    activate (left neighbor);
    if right neighbor is waiting for his left fork
    then
    activate( right neighbor);
   {think}
forever

Code 

C




#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
 
#define N 5
#define THINKING 2
#define HUNGRY 1
#define EATING 0
#define LEFT (phnum + 4) % N
#define RIGHT (phnum + 1) % N
 
int state[N];
int phil[N] = { 0, 1, 2, 3, 4 };
 
sem_t mutex;
sem_t S[N];
 
void test(int phnum)
{
    if (state[phnum] == HUNGRY
        && state[LEFT] != EATING
        && state[RIGHT] != EATING) {
        // state that eating
        state[phnum] = EATING;
 
        sleep(2);
 
        printf("Philosopher %d takes fork %d and %d\n",
                      phnum + 1, LEFT + 1, phnum + 1);
 
        printf("Philosopher %d is Eating\n", phnum + 1);
 
        // sem_post(&S[phnum]) has no effect
        // during takefork
        // used to wake up hungry philosophers
        // during putfork
        sem_post(&S[phnum]);
    }
}
 
// take up chopsticks
void take_fork(int phnum)
{
 
    sem_wait(&mutex);
 
    // state that hungry
    state[phnum] = HUNGRY;
 
    printf("Philosopher %d is Hungry\n", phnum + 1);
 
    // eat if neighbours are not eating
    test(phnum);
 
    sem_post(&mutex);
 
    // if unable to eat wait to be signalled
    sem_wait(&S[phnum]);
 
    sleep(1);
}
 
// put down chopsticks
void put_fork(int phnum)
{
 
    sem_wait(&mutex);
 
    // state that thinking
    state[phnum] = THINKING;
 
    printf("Philosopher %d putting fork %d and %d down\n",
           phnum + 1, LEFT + 1, phnum + 1);
    printf("Philosopher %d is thinking\n", phnum + 1);
 
    test(LEFT);
    test(RIGHT);
 
    sem_post(&mutex);
}
 
void* philosopher(void* num)
{
 
    while (1) {
 
        int* i = num;
 
        sleep(1);
 
        take_fork(*i);
 
        sleep(0);
 
        put_fork(*i);
    }
}
 
int main()
{
 
    int i;
    pthread_t thread_id[N];
 
    // initialize the semaphores
    sem_init(&mutex, 0, 1);
 
    for (i = 0; i < N; i++)
 
        sem_init(&S[i], 0, 0);
 
    for (i = 0; i < N; i++) {
 
        // create philosopher processes
        pthread_create(&thread_id[i], NULL,
                       philosopher, &phil[i]);
 
        printf("Philosopher %d is thinking\n", i + 1);
    }
 
    for (i = 0; i < N; i++)
 
        pthread_join(thread_id[i], NULL);
}


Note – The below program may compile only with C compilers with semaphore and thread library.

The Dining Philosopher Problem is a classic synchronization problem in computer science that involves multiple processes (philosophers) sharing a limited set of resources (forks) in order to perform a task (eating). In order to avoid deadlock or starvation, a solution must be implemented that ensures that each philosopher can access the resources they need to perform their task without interference from other philosophers.

One common solution to the Dining Philosopher Problem uses semaphores, a synchronization mechanism that can be used to control access to shared resources. In this solution, each fork is represented by a semaphore, and a philosopher must acquire both the semaphore for the fork to their left and the semaphore for the fork to their right before they can begin eating. If a philosopher cannot acquire both semaphores, they must wait until they become available.

The steps for the Dining Philosopher Problem solution using semaphores are as follows

1. Initialize the semaphores for each fork to 1 (indicating that they are available).

2. Initialize a binary semaphore (mutex) to 1 to ensure that only one philosopher can attempt to pick up a fork at a time.

3. For each philosopher process, create a separate thread that executes the following code:

  • While true:
    • Think for a random amount of time.
    • Acquire the mutex semaphore to ensure that only one philosopher can attempt to pick up a fork at a time.
    • Attempt to acquire the semaphore for the fork to the left.
  • If successful, attempt to acquire the semaphore for the fork to the right.
  • If both forks are acquired successfully, eat for a random amount of time and then release both semaphores.
  • If not successful in acquiring both forks, release the semaphore for the fork to the left (if acquired) and then release the mutex semaphore and go back to thinking.

4. Run the philosopher threads concurrently.

By using semaphores to control access to the forks, the Dining Philosopher Problem can be solved in a way that avoids deadlock and starvation. The use of the mutex semaphore ensures that only one philosopher can attempt to pick up a fork at a time, while the use of the fork semaphores ensures that a philosopher can only eat if both forks are available.

Overall, the Dining Philosopher Problem solution using semaphores is a classic example of how synchronization mechanisms can be used to solve complex synchronization problems in concurrent programming.

 Implementation of the Dining Philosopher Problem solution using semaphores in Python

Python3




import threading
import time
import random
 
# Define the number of philosophers and forks
num_philosophers = 5
num_forks = num_philosophers
 
# Define semaphores for the forks and the mutex
forks = [threading.Semaphore(1) for i in range(num_forks)]
mutex = threading.Semaphore(1)
 
# Define the philosopher thread function
def philosopher(index):
    while True:
        print(f"Philosopher {index} is thinking...")
        time.sleep(random.randint(1, 5))
         
        mutex.acquire()
         
        left_fork_index = index
        right_fork_index = (index + 1) % num_forks
         
        forks[left_fork_index].acquire()
        forks[right_fork_index].acquire()
         
        mutex.release()
         
        print(f"Philosopher {index} is eating...")
        time.sleep(random.randint(1, 5))
         
        forks[left_fork_index].release()
        forks[right_fork_index].release()
 
# Create a thread for each philosopher
philosopher_threads = []
for i in range(num_philosophers):
    philosopher_threads.append(threading.Thread(target=philosopher, args=(i,)))
     
# Start the philosopher threads
for thread in philosopher_threads:
    thread.start()
     
# Wait for the philosopher threads to complete
for thread in philosopher_threads:
    thread.join()


Output:

Philosopher 0 is thinking...
Philosopher 1 is thinking...
Philosopher 2 is thinking...
Philosopher 3 is thinking...
Philosopher 4 is thinking...
Philosopher 1 is eating...
Philosopher 2 is eating...
Philosopher 0 is eating...
Philosopher 4 is eating...
Philosopher 3 is eating...
Philosopher 1 is thinking...
Philosopher 2 is thinking...
Philosopher 3 is thinking...
Philosopher 4 is thinking...
Philosopher 0 is thinking...
Philosopher 3 is eating...
Philosopher 4 is eating...
Philosopher 0 is eating...
Philosopher 1 is eating...
Philosopher 2 is eating...
Philosopher 0 is thinking...
Philosopher 1 is thinking...
Philosopher 2 is thinking...
Philosopher 3 is thinking...
Philosopher 4 is thinking...

As you can see from the output, each philosopher takes turns thinking and eating, and there is no deadlock or starvation.

Problem with Dining Philosopher

We have demonstrated that no two nearby philosophers can eat at the same time from the aforementioned solution to the dining philosopher problem. The problem with the above solution is that it might result in a deadlock situation. If every philosopher picks their left chopstick simultaneously, a deadlock results, and no philosopher can eat. This situation occurs when this happens.

Some of the solutions include the following: 

  1. The maximum number of philosophers at the table should not exceed four; in this case, philosopher P3 will have access to chopstick C4; he will then begin eating, and when he is finished, he will put down both chopsticks C3 and C4; as a result, semaphore C3 and C4 will now be incremented to 1. 
  2. Now that philosopher P2, who was holding chopstick C2, will also have chopstick C3, he will do the same when finished eating, allowing other philosophers to eat.
  3. While a philosopher in an odd position should select the right chopstick first, a philosopher in an even position should select the left chopstick and then the right chopstick.
  4. A philosopher should only be permitted to choose their chopsticks if both of the available chopsticks (the left and the right) are available at the same time.
  5. All four of the initial philosophers (P0, P1, P2, and P3) should choose the left chopstick before choosing the right, while P4 should choose the right chopstick before choosing the left. As a result, P4 will be forced to hold his right chopstick first because his right chopstick, C0, is already being held by philosopher P0 and its value is set to 0. As a result, P4 will become trapped in an infinite loop and chopstick C4 will remain empty. As a result, philosopher P3 has both the left C3 and the right C4. chopstick available, therefore it will start eating and will put down both chopsticks once finishes and let others eat which removes the problem of deadlock.

FAQs on Dining Philosopher Problem Using Semaphores

Q.1: What is the main challenge in the dining philosophers problem?

Answers: 

The main challenge in the dining philosophers’ problem is preventing deadlocks. Deadlock occurs when each philosopher picks up one chopstick and is waiting indefinitely for the other chopstick. This situation leads to a system-wide halt, and no philosopher can make progress. Resolving the deadlock issue while allowing all philosophers to eat without starvation requires careful synchronization and resource allocation.

Q.2: How can the dining philosophers problem be solved?

Answer:

Several solutions exist to solve the dining philosophers problem and prevent deadlocks. Some common approaches include:

Resource hierarchy: Assign a total order to the chopsticks and have each philosopher pick up the lower-numbered chopstick first. This prevents circular dependencies and guarantees that deadlock cannot occur.

Chandy/Misra solution: Introduce an arbitrator or central authority that manages the allocation of chopsticks. Philosophers request permission from the arbitrator before attempting to pick up the chopsticks, ensuring that only a limited number of philosophers can hold chopsticks simultaneously.

Dijkstra’s solution using semaphores: Assign a semaphore to each chopstick, representing its availability. Philosophers must acquire both chopsticks by locking their corresponding semaphores. If a philosopher cannot acquire both chopsticks, they release the acquired chopstick and wait before trying again.

Timeout-based solution: Set a timer or timeout for each philosopher. If a philosopher cannot acquire both chopsticks within a certain time limit, they release the acquired chopstick and retry later. This approach avoids deadlocks but may result in starvation if philosophers keep getting interrupted.

Q.3: Can the dining philosophers problem have multiple solutions?

Answer: 

Yes, the dining philosophers problem can have multiple solutions. The choice of solution depends on the specific requirements of the system and the desired behavior. Different algorithms and approaches can be employed to ensure the philosophers can eat without deadlocks and minimize the occurrence of starvation. The suitability of a particular solution depends on factors such as the number of philosophers, the desired fairness, efficiency, and the overall system architecture.



Last Updated : 07 Nov, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads