Why do we need a condition check before pthread_co

2019-03-15 00:45发布

问题:

I am trying to learn basics of pthread_cond_wait. In all the usages, I see either

if(cond is false)
   pthread_cond_wait

or

while(cond is false)
   pthread_cond_wait

My question is, we want to cond_wait only because condition is false. Then why should i take the pain of explicitly putting an if/while loop. I can understand that without any if/while check before cond_wait we will directly hit that and it wont return at all. Is the condition check solely for solving this purpose or does it have anyother significance. If it for solving an unnecessary condition wait, then putting a condition check and avoiding the cond_wait is similar to polling?? I am using cond_wait like this.

void* proc_add(void *name){
    struct vars *my_data = (struct vars*)name;
    printf("In thread Addition and my id = %d\n",pthread_self());
    while(1){
    pthread_mutex_lock(&mutexattr);
    while(!my_data->ipt){  // If no input get in
            pthread_cond_wait(&mutexaddr_add,&mutexattr);  // Wait till signalled
            my_data->opt = my_data->a + my_data->b;
            my_data->ipt=1;
            pthread_cond_signal(&mutexaddr_opt);
    }
    pthread_mutex_unlock(&mutexattr);
    if(my_data->end)
            pthread_exit((void *)0);
    }
}

The logic is, I am asking the input thread to process the data whenever an input is available and signal the output thread to print it.

回答1:

You need a while loop because the thread that called pthread_cond_wait might wake up even when the condition you are waiting for isn't reached. This phenomenon is called "spurious wakeup".

This is not a bug, it is the way the conditional variables are implemented.

This can also be found in man pages:

Spurious wakeups from the pthread_cond_timedwait() or pthread_cond_wait() functions may occur. Since the return from pthread_cond_timedwait() or pthread_cond_wait() does not imply anything about the value of this predicate, the predicate should be re-evaluated upon such return.

Update regarding the actual code:

void* proc_add(void *name) 
{
    struct vars *my_data = (struct vars*)name;

    printf("In thread Addition and my id = %d\n",pthread_self());

    while(1) {

        pthread_mutex_lock(&mutexattr);

        while(!my_data->ipt){  // If no input get in
            pthread_cond_wait(&mutexaddr_add,&mutexattr);  // Wait till signalled
        }

        my_data->opt = my_data->a + my_data->b;
        my_data->ipt=1;
        pthread_cond_signal(&mutexaddr_opt);

        pthread_mutex_unlock(&mutexattr);

        if(my_data->end)
            pthread_exit((void *)0);
        }
    }
}


回答2:

You must test the condition under the mutex before waiting because signals of the condition variable are not queued (condition variables are not semaphores). That is, if a thread calls pthread_cond_signal() when no threads are blocked in pthread_cond_wait() on that condition variable, then the signal does nothing.

This means that if you had one thread set the condition:

pthread_mutex_lock(&m);
cond = true;
pthread_cond_signal(&c);
pthread_mutex_unlock(&m);

and then another thread unconditionally waited:

pthread_mutex_lock(&m);
pthread_cond_wait(&c, &m);
/* cond now true */

this second thread would block forever. This is avoided by having the second thread check for the condition:

pthread_mutex_lock(&m);
if (!cond)
    pthread_cond_wait(&c, &m);
/* cond now true */

Since cond is only modified with the mutex m held, this means that the second thread waits if and only if cond is false.

The reason a while () loop is used in robust code instead of an if () is because pthread_cond_wait() does not guarantee that it will not wake up spuriously. Using a while () also means that signalling the condition variable is always perfectly safe - "extra" signals don't affect the program's correctness, which means that you can do things like move the signal outside of the locked section of code.