Sreeraj Chundayil
Sreeraj Chundayil

Reputation: 5859

Why pthread_condition variable getting hanged?

I just started learning pthread condition variable. But below code is not working as expected.

#include<iostream>
#include<pthread.h>

using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t count_threshold_cv = PTHREAD_COND_INITIALIZER;
int count=0;


void *inc_func(void *arg)
{
    pthread_mutex_lock(&mutex);
    int c;

    while(1)
    {
        cin>>c;
        if(c==8){ 
            pthread_cond_signal(&count_threshold_cv);break;}
    }

    cout<<"inc count reached 8"<<endl;
    pthread_mutex_unlock(&mutex);

}

void *watch(void *arg)
{
    pthread_mutex_lock(&mutex);
    while(1){
        pthread_cond_wait(&count_threshold_cv,&mutex);
        break;
    }
    cout<<"This executed"<<endl;
    pthread_mutex_unlock(&mutex);
}

int main()
{
    pthread_t id[26];
    pthread_create(&id[0],NULL,inc_func,NULL);
    pthread_create(&id[1],NULL,watch,NULL);

    int i;
    for(i=0;i<2;i++)
    {
        pthread_join(id[i],NULL);
    }

}

when the input is 8 this code gets hanged at "inc count reached 8? I am not able to understand. Where is my understanding wrong?

Upvotes: 1

Views: 322

Answers (3)

caf
caf

Reputation: 239011

The correct solution to this is to make the watch thread only wait if the condition it is waiting for has not occured yet.

The condition appears to be c == 8 (since that is what is signalled), so you will need to make the c variable global so that it is shared between the threads, then change the watch thread to do:

void *watch(void *arg)
{
    pthread_mutex_lock(&mutex);
    while (c != 8) {
        pthread_cond_wait(&count_threshold_cv, &mutex);
    }
    cout<<"This executed"<<endl;
    pthread_mutex_unlock(&mutex);

    return 0;
}

Now it doesn't matter which thread runs first: your code is correct either way. This is the right way to use conditional variables: in general, the waiter should do:

pthread_mutex_lock(&mutex);

while (!condition)
    pthread_cond_wait(&cond, &mutex);

/* ... */

and the signaller should do:

pthread_mutex_lock(&mutex);

/* ... something that potentially makes condition true ... */

pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);

Upvotes: 3

Ionut
Ionut

Reputation: 6866

The important thing here is that pthread_cond_signal will unblock at least one of the threads that are blocked on that condition variable (meaning which are currently blocked on a call to pthread_cond_wait on that same condition variable). If at the moment when one thread calls pthread_cond_signal there is no other thread waiting on that condition then basically nothing happens.

Keeping this in mind, the flow of your program is something like this:

  • create and start first thread;
  • first thread calls inc_func(), which locks the mutex before anything else;
  • inc_func() keeps waiting for the number 8 to be entered, keeping the mutex locked all this time;
  • sometime during this, but most times probably after the inc_func managed to lock the mutex, the second thread is created;
  • the second thread also tries to lock the mutex right at the start of the function, and is blocked because the first thread already has it locked;
  • at some point, you enter 8 and the condition gets signaled from thread 1; thread 2 is not waiting on this condition yet, so it remains blocked trying to lock the mutex;
  • the first thread finally releases the mutex, so thread 2 locks it and then blocks on pthread_cond_wait.

At this point, thread 1 has already finished, thread 2 is blocked waiting for the condition to be signaled, and the main thread is waiting for it to finish. There is nobody else to signal that condition, so you have a hang.

For a quick fix that will probably work most of the times, you could try changing the order in which you start the threads(start the watch thread first). But keep in mind and understand why I used bold for probably and most of the times. The correct way to fix this would be to rethink your locking strategy: lock the mutexes in the smallest scope possible and keep them locked for the shortest time possible.

Upvotes: 2

prajmus
prajmus

Reputation: 3271

Swap the thread's execution sequence:

pthread_create(&id[1],NULL,watch,NULL);
pthread_create(&id[0],NULL,inc_func,NULL);

If you run thread0 as first, thread1 never gets past mutex lock, so it doesn't start waiting. Than thread0 ends, and only then thread1 executes pthread_cond_wait(), but there's no thread to do signal.

If you start thread1 first, it gets to the waiting part.

Upvotes: 1

Related Questions