Lecture 15: Dining Philosophers Problem
Review: Mutual Exclusion Solutions Software solution Disabling interrupts Strict alternation Peterson’s solution Hardware solution TSL/XCHG Semaphore Conditional variables POSIX standards
Review: Pthreads APIs for condition variables
Review: Using condition variables int thread1_done = 0; pthread_cond_t cv; pthread_mutex_t mutex; Thread 1: printf(“hello “); pthread_mutex_lock(&mutex); thread1_done = 1; pthread_cond_signal(&cv); pthread_mutex_unlock(&mutex); Thread 2: pthread_mutex_lock(&mutex); while (thread1_done == 0) { pthread_cond_wait(&cv, &mutex); } printf(“ world\n“); pthread_mutex_unlock(&mutex);
Review: Deadlock thread a thread b proc1( ) { pthread_mutex_lock(&m1); /* use object 1 */ pthread_mutex_lock(&m2); /* use objects 1 and 2 */ pthread_mutex_unlock(&m2); pthread_mutex_unlock(&m1); } proc2( ) { pthread_mutex_lock(&m2); /* use object 2 */ pthread_mutex_lock(&m1); /* use objects 1 and 2 */ pthread_mutex_unlock(&m1); pthread_mutex_unlock(&m2); } In this example our threads are using two mutexes to control access to two different objects. Thread 1, executing proc1, first takes mutex 1, then, while still holding mutex 1, obtains mutex 2. Thread 2, executing proc2, first takes mutex 2, then, while still holding mutex 2, obtains mutex 1. However, things do not always work out as planned. If thread 1 obtains mutex 1 and, at about the same time, thread 2 obtains mutex 2, then if thread 1 attempts to take mutex 2 and thread 2 attempts to take mutex 1, we have a deadlock. thread a thread b
In this lecture Dining Philosophers Problem Readers-Writers Problem
Dining Philosophers Classic Synchronization Problem Philosopher eat, think …….. Philosopher = Process Eating needs two resources (chopsticks)
Problem: need two chopsticks to eat
First Pass at a Solution One Mutex for each chopstick Philosopher i: while (1) { Think(); lock(Left_Chopstick); lock(Right_Chopstick); Eat(); unlock(Left_Chopstick); unlock(Right_Chopstick); }
DEADLOCK
One Possible Solution Use a mutex for the whole dinner-table Philosopher i: lock(table); Eat(); Unlock(table); Performance problem!
Another Solution Problem: starvation if unfavorable scheduling! Philosopher i: Think(); unsuccessful = 1; while (unsuccessful) { lock(left_chopstick); if (try_lock(right_chopstick)) /* try_lock returns immediately if unable to grab the lock */ unsuccessful = 0; else unlock(left_chopstick); } Eat(); unlock(right_chopstick); Problem: starvation if unfavorable scheduling!
In Practice Starvation will probably not occur We can ensure this by adding randomization to the system: Add a random delay before retrying Unlikely that our random delays will be in sync too many times
Solution with Random Delays Philosopher i: Think(); while (unsuccessful) { wait(random()); lock(left_chopstick); if (trylock(right_chopstick)) unsuccessful = 0; else unlock(left_chopstick); } Eat(); unlock(right_chopstick);
Solution without random delay Do not try to take forks one after another Don’t have each fork protected by a different mutex Try to grab both forks at the same time