Search code examples
cmultithreadingmutexcondition-variable

Conditional Variables with multithreading


I am working on the dining philosophers problem, where n philosophers take turns thinking and eating. I would like to have a version of this where the philosophers will eat in the order of their id: 0,1,2,3,4...,but my threads keep getting blocked. My threads start by calling PhilosopherThread().

void putDownChopsticks(int threadIndex){
     //finished eating
     pindex++;
     pthread_cond_signal(&cond);
     pthread_mutex_unlock(&lock);
}

void pickUpChopsticks(int threadIndex){
     pthread_mutex_lock(&lock);
     while(pindex != threadIndex){
         pthread_cond_wait(&cond, &lock);
     } 
     //lets go eat
} 

void eating(){
     //put thread to sleep
}

void thinking(){
     //put thread to sleep
}

void* PhilosopherThread(void *x){
     int *index = x;
     thinking(); //just puts thread to sleep to simulate thinking
     pickUpChopsticks(*index);
     eating(); //just puts thread to sleep to simulate eating
     putDownChopsticks(*index);
     return NULL;
}

I'm having a bit of trouble trying to get the philosophers in order. I can only get the first 2 threads to eat before the threads get blocked.


Edit: as far as i know im doing this right. I first lock the mutex, then I check if pindex is the current thread id, if its not the thread will wait until pindex does equal the id. Then the thread can go eat and once where done, we incread pindex, signal that the thread is done, and unlock the mutex.


Solution

  • This code sometimes works and sometimes does not. First, since you did not provide a complete program, here are the missing bits I used for testing purposes:

    #include <stdlib.h>
    #include <pthread.h>
    
    static pthread_cond_t cond;
    static pthread_mutex_t lock;
    static pindex;
    
    /* ... your code ... */
    
    int main () {
        int id[5], i;
        pthread_t tid[5];
        for (i = 0; i < 5; ++i) {
            id[i] = i;
            pthread_create(tid+i, 0, PhilosopherThread, id+i);
        }
        for (i = 0; i < 5; ++i) pthread_join(tid[i], 0);
        exit(0);
    }
    

    The critical piece to notice is how you wake up the next philosopher:

     pthread_cond_signal(&cond);
    

    This call will only wake up one thread. But, which thread is at the discretion of the OS. Therefore, if it does not happen to wake up the philosopher that is supposed to wake up, no other philosopher is woken up.

    A simple fix would be to wake up all waiting threads instead of just one. The philosophers that don't match will go back to waiting, and the one that is supposed to go next will go.

     pthread_cond_broadcast(&cond);
    

    However, since each thread knows which philosopher should wake up, you could change your solution to allow that to happen. One way could be to implement a separate condition variable per philosopher, and use pthread_cond_signal() on the next philosopher's condition variable.