0
votes

Does anyone ever try an example from https://computing.llnl.gov/tutorials/pthreads/samples/condvar.c

This example call 3 threads, a main thread watch_count() executes until reaching pthread_cond_wait() and the other 2 threads inc_count() increment a "count" in turn. when the count is equal to the predetermined threshold, pthread_cond_signal() is called then pthread of inc_count() is unlocked and going back to the code (locked) following pthread_cond_wait() in the main thread. However, i found sometimes when inc_count() reaches pthread_cond_signal(), afeter this thread is unlocked and the lock performed by pthread_cond_wait() in watch_count(), another inc_count() executes, so the count is incremented "again". However, this shouldn't happen theoretically. I found that when i adjust the count limit, the behavior is different.

Please see the following photo, the top one is wrong because when thread3 reaches the threshold (11), it sends the signal. then, before arriving watch_count(), thread 2 intercepts.

However, when COUNT_LIMIT=12, everything works exactly as i thought

Can any one help me?

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

#define NUM_THREADS  3
#define TCOUNT 10
#define COUNT_LIMIT 12

int     count = 0;
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;

void *inc_count(void *t) 
{
  int i;
  long my_id = (long)t;

  for (i=0; i < TCOUNT; i++) {
    pthread_mutex_lock(&count_mutex);
    count++;

    /* 
    Check the value of count and signal waiting thread when condition is
    reached.  Note that this occurs while mutex is locked. 
    */
    if (count == COUNT_LIMIT) {
      printf("inc_count(): thread %ld, count = %d  Threshold reached. ",
             my_id, count);
      pthread_cond_signal(&count_threshold_cv);
      printf("Just sent signal.\n");
      }
    printf("inc_count(): thread %ld, count = %d, unlocking mutex\n", 
       my_id, count);
    pthread_mutex_unlock(&count_mutex);

    /* Do some work so threads can alternate on mutex lock */
    sleep(1);
    }
  pthread_exit(NULL);
}

void *watch_count(void *t) 
{
  long my_id = (long)t;

  printf("Starting watch_count(): thread %ld\n", my_id);

  /*
  Lock mutex and wait for signal.  Note that the pthread_cond_wait routine
  will automatically and atomically unlock mutex while it waits. 
  Also, note that if COUNT_LIMIT is reached before this routine is run by
  the waiting thread, the loop will be skipped to prevent pthread_cond_wait
  from never returning.
  */
  pthread_mutex_lock(&count_mutex);
  while (count < COUNT_LIMIT) {
    printf("watch_count(): thread %ld Count= %d. Going into wait...\n", my_id,count);
    pthread_cond_wait(&count_threshold_cv, &count_mutex);
    printf("watch_count(): thread %ld Condition signal received. Count= %d\n", my_id,count);
    printf("watch_count(): thread %ld Updating the value of count...\n", my_id,count);
    count += 125;
    printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
    }
  printf("watch_count(): thread %ld Unlocking mutex.\n", my_id);
  pthread_mutex_unlock(&count_mutex);
  pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
  int i, rc; 
  long t1=1, t2=2, t3=3;
  pthread_t threads[3];
  pthread_attr_t attr;

  /* Initialize mutex and condition variable objects */
  pthread_mutex_init(&count_mutex, NULL);
  pthread_cond_init (&count_threshold_cv, NULL);

  /* For portability, explicitly create threads in a joinable state */
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  pthread_create(&threads[0], &attr, watch_count, (void *)t1);
  pthread_create(&threads[1], &attr, inc_count, (void *)t2);
  pthread_create(&threads[2], &attr, inc_count, (void *)t3);

  /* Wait for all threads to complete */
  for (i = 0; i < NUM_THREADS; i++) {
    pthread_join(threads[i], NULL);
  }
  printf ("Main(): Waited and joined with %d threads. Final value of count = %d. Done.\n", 
          NUM_THREADS, count);

  /* Clean up and exit */
  pthread_attr_destroy(&attr);
  pthread_mutex_destroy(&count_mutex);
  pthread_cond_destroy(&count_threshold_cv);
  pthread_exit (NULL);

}

enter image description here

1

1 Answers

0
votes

The inc_count() threads will each add 1 to the count TCOUNT times. The one that increments the count to COUNT_LIMIT will cond_signal the watch_count() thread. The magic of printf() is that you will see output from each thread interleaved reasonably sensibly.

You appear to be assuming that as soon as the pthread_cond_signal() is done, the watch_count() thread will run. It may do so, but will immediately stop on the mutex at least until the signalling thread unlocks the mutex. If the other inc_count() thread is also waiting on the mutex at that moment, then it may run before or after the watch_count() thread. It may be that threads wait on the mutex in fifo order... but since you have no idea what order they arrived in, that doesn't allow you to predict what order they will run in !


Further: assuming that watch_count() is intended to wait until the count >= COUNT_LIMIT before it updates the count, then stuff should be moved out of the while() loop:

  while (count < COUNT_LIMIT) {
    printf("watch_count(): thread %ld Count= %d. Going into wait...\n", my_id,count);
    pthread_cond_wait(&count_threshold_cv, &count_mutex);
    printf("watch_count(): thread %ld Condition signal received. Count= %d\n", my_id,count);
    }
  printf("watch_count(): thread %ld Updating the value of count...\n", my_id,count);
  count += 125;
  printf("watch_count(): thread %ld count now = %d.\n", my_id, count);

The key thing to understand is that the condition (so called) "variable" has no memory at all, so before waiting for some state to be set, you need to check that the state has not already been set (and the condition already signalled -- which only has effect if there is a waiter, and is forgotten if there is none).

The other issue is that conditions can (under some circumstances) release more than one waiter when they are signalled... so it is conventional to put a while loop around the pthread_cond_wait() and to use some state (under the mutex) to control how many waiters continue after a pthread_cond_signal().