0

I tested out two very simple examples of printing even/odd numbers in multithreaded code, one using pthread_cond_t and the other not.

void *even(void *arg)
{

    while(count < MAX) 
    {   
        pthread_mutex_lock(&mutex);
        if(count % 2 == 0)
            printf("%d ", count++);
        pthread_mutex_unlock(&mutex);
    }   
    pthread_exit(0);
}   

void *odd(void *arg)
{

    while(count < MAX)
    {
        pthread_mutex_lock(&mutex);
        if(count % 2 == 1) 
            printf("%d ", count++);
        pthread_mutex_unlock(&mutex);
    }   
    pthread_exit(0);
}  

void *even(void *arg)
{   
    while(count < MAX) {
        pthread_mutex_lock(&mutex);
        while(count % 2 != 0) {
            pthread_cond_wait(&cond, &mutex);
        }   
        printf("%d ", count++);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
    }
    pthread_exit(0);
}

void *odd(void *arg)
{   
    while(count < MAX) {
        pthread_mutex_lock(&mutex);
        while(count % 2 != 1) {
            pthread_cond_wait(&cond, &mutex);
        }
        printf("%d ", count++);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
    }
    pthread_exit(0);
}

The above two codes behave differently. 1st code output:

0 1 2 3 4 5 6 7 8 9 10
0 1 2 3 4 5 6 7 8 9 10
0 1 2 3 4 5 6 7 8 9 10
0 1 2 3 4 5 6 7 8 9  
0 1 2 3 4 5 6 7 8 9 10 
0 1 2 3 4 5 6 7 8 9 10 
0 1 2 3 4 5 6 7 8 9  

2nd code output:

0 1 2 3 4 5 6 7 8 9 10 
0 1 2 3 4 5 6 7 8 9 10 
0 1 2 3 4 5 6 7 8 9 10 
0 1 2 3 4 5 6 7 8 9 10 

The reason for the inconsistent output for 1st code could be that:

in between the following calls in even thread: suppose count == 8

pthread_mutex_unlock(&mutex);
..... --> here, the odd thread increments the count by one before this thread could check the the following while condition 
while(count < MAX)

so 10 is missed occasionally.

But for the code using pthread_cond_wait(), there is no such inconsistency, although, the same argument can be made for it aswell: in between these calls in even thread:suppose count == 8

pthread_cond_signal(&cond);// mutex is already unlocked before condition is signaled.
.... --> here, the odd thread can increment the count to 10 before the even thread could check the while condition
while(count < MAX)

But in practice,this never happend for 2nd code, so somehow, pthread_cond_wait() code takes care of such inconsistency, but it seems unclear to me how?

Is there anything else going on behind the scenes in pthread_cond_wait() that helps out?

Thanks

ashishsony
  • 2,537
  • 3
  • 26
  • 38
  • 1
    You can actually signal the condition *before* releasing the mutex... to avoid the sort of race you mention. – Dmitri Jul 07 '15 at 09:23
  • What value of `MAX` constant? It seems, that it is 10, so both variants produce (generally) incorrect output: it should be 0...9. – Tsyvarev Jul 07 '15 at 09:42
  • Yes,MAX is defined as 10. – ashishsony Jul 07 '15 at 09:49
  • @Dmitri i think, if i signal the condition, before releasing the mutex, the race would be more probable. as the waiting thread(odd thread) would wake up,and fight for the mutex, and as soon as it gets the lock, it would increment count, and there is high probability that this happens before the even thread can check on count. – ashishsony Jul 07 '15 at 10:00
  • Lock the mutex *before* the loop, and unlock *after* (do neither inside the loop)... then each thread can only hold the mutex while the other thread is waiting, and neither will ever read/write `count` without holding the mutex. – Dmitri Jul 07 '15 at 10:22
  • Related: http://stackoverflow.com/q/1640389/694576 – alk Jul 18 '15 at 16:23

1 Answers1

2

Both code snippets has the same problem. Both versions have race condition. You are reading count outside the critical section and other thread might modify count in the meantime. You just happen to notice in the version without conditional variable.

Instead of reading count for loop condition, you can use flag that's local to each thread and change it inside the loop for breaking the loop.

Something like:

void *even(void *arg)
{
int flag = 1;

    while(flag) {
        pthread_mutex_lock(&mutex);
        while(count % 2 != 0) {
            pthread_cond_wait(&cond, &mutex);
        }
        printf("%d ", count++);
        if (count >MAX) flag = 0;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
    }
    pthread_exit(0);
}

void *odd(void *arg)
{
int flag = 1;

    while(flag) {
        pthread_mutex_lock(&mutex);
        while(count % 2 != 1) {
            pthread_cond_wait(&cond, &mutex);
        }
        printf("%d ", count++);
        if (count >MAX) flag = 0;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond);
    }
    pthread_exit(0);
}
P.P
  • 117,907
  • 20
  • 175
  • 238