bhr
bhr

Reputation: 131

producer consumer synchronizing11

#include <stdio.h>
#include <pthread.h>
#define MAX 10                                  /* maximum iterations */
int number;                                     /* the resource */
pthread_mutex_t mu= PTHREAD_MUTEX_INITIALIZER;  /* to protect the resource*/
/*
    Condition variable to signal consumer that a new number is available for
    consumption.
*/
pthread_cond_t sig_consumer= PTHREAD_COND_INITIALIZER;
/*
      Condition variable to signal the producer that
      (a) the new number has been consumed,
      (b) generate another one.
*/
pthread_cond_t sig_producer= PTHREAD_COND_INITIALIZER;
void *consumer(void *dummy)
{
      int printed= 0;
      printf("Consumer : \"Hello I am consumer #%ld. Ready to consume numbers"
             " now\"\n", pthread_self());

      while (1)
      {
         pthread_mutex_lock(&mu);
         /* Signal the producer that the consumer is ready. */
         pthread_cond_signal(&sig_producer);
         /* Wait for a new number. */
         pthread_cond_wait(&sig_consumer, &mu);
         /* Consume (print) the number. */
         printf("Consumer : %d\n", number);
         /* Unlock the mutex. */
         pthread_mutex_unlock(&mu);

         /*
           If the MAX number was the last consumed number, the consumer should
           stop.
         */
         if (number == MAX)
         {
           printf("Consumer done.. !!\n");
           break;
         }
      }
}
        /**
          @func producer
          This function is responsible for incrementing the number and signalling the
          consumer.
        */
void *producer(void *dummy)
{
      printf("Producer : \"Hello I am producer #%ld. Ready to produce numbers"
             " now\"\n", pthread_self());
      while (1)
      {
            pthread_mutex_lock(&mu);
            number ++;
            printf("Producer : %d\n", number);
            /*
              Signal the consumer that a new number has been generated for its
              consumption.
            */
            pthread_cond_signal(&sig_consumer);
            /*
              Now wait for consumer to confirm. Note, expect no confirmation for
              consumption of MAX from consumer.
            */
            if (number != MAX)
              pthread_cond_wait(&sig_producer, &mu);

            /* Unlock the mutex. */
            pthread_mutex_unlock(&mu);

            /* Stop if MAX has been produced. */
            if (number == MAX)
            {
              printf("Producer done.. !!\n");
              break;
            }
      }
}

void main()
{
      int rc, i;
      pthread_t t[2];
      number= 0;
      /* Create consumer & producer threads. */
      if ((rc= pthread_create(&t[0], NULL, consumer, NULL)))
        printf("Error creating the consumer thread..\n");
      if ((rc= pthread_create(&t[1], NULL, producer, NULL)))
        printf("Error creating the producer thread..\n");

      /* Wait for consumer/producer to exit. */
      for (i= 0; i < 2; i ++)
        pthread_join(t[i], NULL);

      printf("Done..\n");
} 

Question: If the consumer thread is started before the producer thread, then the program will provide the expected result, but if the producer is started first, then the consumer will start consuming from number 2 onwards; the consumer is unable consume number 1. How to correct the program even though the producer thread starts first, without introducing any additional variable or sleep?

Upvotes: 0

Views: 181

Answers (1)

user3793679
user3793679

Reputation:

The problem with pthread_cond_t is its name. Despite being nominally a "condition", it has no state... in particular, it does not remember that it has been signalled at all -- if you thought it might count how many times it has been signalled, you will be disappointed (for that you need a semaphore). To put that another way, if there is no pthread waiting on a condition when it is signalled, the signal has no effect and is forgotten.

The "condition" is better thought of as a "wait queue", where pthreads wait for some state to be updated. So usually you have some state, protected by the mutex. If the state is not as required for the pthread to continue, then the pthread waits on the "condition". When the state is updated, the "condition" can be signalled. When the waiter wakes up, it must check the state, and decide whether everything is now ready to continue.

Where there are two or more pthreads waiting, the standard allows pthread_cond_signal() to wake up one, two or more, or all, the waiters. The mutex ensures that the waiters' access to the state is serialised, but the waiter cannot (in general, and for this reason in particular) assume that the state is unchanged since the signal. So, it is common to write the waiter:

pthread_mutex_lock(&mutex) ;
....
while(...what we need to continue...)
  pthread_cond_wait(&cond, &mutex) ;
....
pthread_mutex_unlock(&mutex) ;

Which reflects the importance of the state, and how little the "condition" contributes.

Upvotes: 1

Related Questions