Prakash GiBBs
Prakash GiBBs

Reputation: 315

Unexpected behavior while using condition variable multithread

In the code below:

#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mtx;
pthread_cond_t cond;

int how_many = 10;
int pool = 0;

void * producer(void * ptr)
{
        while (how_many > 0)
        {
                pthread_mutex_lock(&mtx);
                printf("producer: %d\n", how_many);
                pool = how_many;
                how_many--;
                pthread_mutex_unlock(&mtx);
                pthread_cond_signal(&cond);
        }

        pthread_exit(0);
}

void * consumer(void * ptr)
{
        while (how_many > 0)
        {
                pthread_mutex_lock(&mtx);
                pthread_cond_wait(&cond, &mtx);
                printf("consumer: %d\n", pool);
                pool = 0;
                pthread_mutex_unlock(&mtx);
        }
        pthread_exit(0);
}

int main(int argc, char ** argv)
{
        pthread_t prod, cons;
        pthread_mutex_init(&mtx, 0);
        pthread_cond_init(&cond, 0);
        pthread_create(&cons, 0, consumer, 0);
        pthread_create(&prod, 0, producer, 0);
        pthread_join(prod, 0);
        pthread_join(cons, 0);
        pthread_cond_destroy(&cond);
        pthread_mutex_destroy(&mtx);
        return 0;
}

I'm not getting the expected output.

Expected output:

Producer:10    
Consumer:10    
Producer:9    
Consumer:9    
Producer:8    
Consumer:8    
Producer:7    
Consumer:7    
Producer:6    
Consumer:6    
Producer:5    
Consumer:5    
Producer:4    
Consumer:4    
Producer:3    
Consumer:3    
Producer:2    
Consumer:2    
Producer:1    
Consumer:1

Actual output:

producer: 10    
producer: 9    
producer: 8    
producer: 7    
producer: 6    
producer: 5    
producer: 4    
producer: 3    
producer: 2    
producer: 1

Also, in consumer side, If we lock and wait for the signal, how the producer can get the lock so that he can send the signal to the consumer?

  1. Will it be dead lock?
  2. My friends are suggesting like pthread_cond_wait(&cond, &mtx); would actually unlock the resources until it gets the signal from producer. Is that true?

Upvotes: 2

Views: 97

Answers (3)

John Bollinger
John Bollinger

Reputation: 180058

Mutexes provide only mutual exclusion (when used properly); they do not themselves provide a mechanism for blocking on a specific event to happen or until a specific condition is satisfied. That's what condition variables are for (and semaphores, if you want to go a little lower-level).

Your code provides for the consumer to wait on the producer to produce, but not for the producer to wait on the consumer to consume before it continues. If you want the two threads to alternate, then you need a second condition variable to provide for the latter.

Also, in consumer side, If we lock and wait for the signal, how the producer can get the lock so that he can send the signal to the consumer?

  1. will it be dead lock?

  2. My friends are suggesting like pthread_cond_wait(&cond, &mtx); would actually unlock the resources until it gets the signal from producer. is that true?

Rather than asking your friends -- or the Internet -- have you considered reading the documentation? Here's the way the man page describes it:

These functions atomically release mutex [...]. Upon successful return, the mutex shall have been locked and shall be owned by the calling thread.

That is, the thread calling pthread_cond_wait() does not hold the mutex locked while it is waiting, but it reacquires the mutex before it returns (which may involve an indeterminate delay between the thread receiving the signal and the function call returning).

Additionally, always remember that a thread can wake up spurriously from waiting on a condition variable. It is essential to check upon wakeup whether the condition is in fact satisfied, and to resume waiting if not.

Here's a way you could structure the producer:

void * producer(void * ptr)
{
        pthread_mutex_lock(&mtx);
        while (how_many > 0)
        {
                if (pool == 0) {
                        printf("producer: %d\n", how_many);
                        pool = how_many;
                        how_many--;
                        pthread_cond_signal(&full_cond);
                }
                pthread_cond_wait(&empty_cond, &mtx);
        }
        pthread_mutex_unlock(&mtx);

        pthread_exit(0);
}

Note that:

  1. I have renamed your original condition variable and introduced a new one. There is now full_cond, indicating that the pool (of capacity 1) is full, and empty_cond, indicating that the pool is empty.
  2. The whole loop is protected by a mutex. This is fine because it performs a pthread_cond_wait() naming that mutex; the other threads will be able to run while the the producer is waiting. The mutex ensures that access to the how_many and pool variables is correctly synchronized.
  3. The loop protects against spurrious wakeup by testing pool to verify that it really is empty. If not, it loops back to the wait without doing anything else.
  4. For this to work correctly, the consumer requires corresponding changes (left as an exercise for you).

Upvotes: 3

BEPP
BEPP

Reputation: 955

For your expected output you can use the locking mechanism like below,

 #include <pthread.h> 
 #include <stdio.h> 
 #include <stdlib.h> 
 #include <semaphore.h> 

 sem_t mutex1; 
 sem_t mutex2; 
 int main()
 {
    pthread_t thread1, thread2; 
    sem_init(&mutex1, 0, 1); 
    sem_init(&mutex2, 0, 0);
    pthread_create( &thread1, NULL, &producer, NULL)
    pthread_create( &thread2, NULL, &consumer, NULL)
    pthread_join( thread1, NULL); 
    pthread_join( thread2, NULL); 
    return 0; 
 }


void producer()
{
  sem_wait(&mutex1); 
  :
  :

  sem_post(&mutex2);
}

void consumer ()
{
    sem_wait(&mutex2); 
    :
    : 
    sem_post(&mutex1);
}   

Upvotes: 0

wilx
wilx

Reputation: 18228

You are checking how_many out of the locked section. You need to restructure you code so that reading the variable is covered by a lock or so that it is C11 _Atomic.

Even then, your code's output is likely not going to be the way you want it as the scheduling of the threads is pretty much unpredictable.

Upvotes: 2

Related Questions