1
votes

I have this code as an example where two threads are created and then it looks like a pthread_cond_wait() is used to suspend that thread until it is ready to work again by the use of pthread_cond_signal(). My question is what if multiple threads are waiting at the same time? How will executing pthread_cond_signal() pick the correct thread to wake up? Is there a way to pick a specific thread to awake? Lets say i have a producer thread that puts customer orders into seperate queues where each queue is managed by a thread. If two consumer threads are suspend with wait() because they have nothing in their queue but then the producer thread only inserts an order into ONE of the consumer queues, how the heck do we differentiate? If this is not possible then what OTHER methods can i use to accomplish what i want?

Here is an example code because stackoverflow likes code... not that relevant: Example

#define _MULTI_THREADED
#include <pthread.h>
#include <stdio.h>
#include "check.h"

/* For safe condition variable usage, must use a boolean predicate and  */
/* a mutex with the condition.                                          */
int                 workToDo = 0;
pthread_cond_t      cond  = PTHREAD_COND_INITIALIZER;
pthread_mutex_t     mutex = PTHREAD_MUTEX_INITIALIZER;

#define NTHREADS      2

void *threadfunc(void *parm)
{
  int           rc;

  while (1) {
    /* Usually worker threads will loop on these operations */
    rc = pthread_mutex_lock(&mutex);
    checkResults("pthread_mutex_lock()\n", rc);

    while (!workToDo) {
      printf("Thread blocked\n");
      rc = pthread_cond_wait(&cond, &mutex);
      checkResults("pthread_cond_wait()\n", rc);
    }
    printf("Thread awake, finish work!\n");

    /* Under protection of the lock, complete or remove the work     */
    /* from whatever worker queue we have. Here it is simply a flag  */
    workToDo = 0;

    rc = pthread_mutex_unlock(&mutex);
    checkResults("pthread_mutex_lock()\n", rc);
  }
  return NULL;
}

int main(int argc, char **argv)
{
  int                   rc=0;
  int                   i;
  pthread_t             threadid[NTHREADS];

  printf("Enter Testcase - %s\n", argv[0]);

  printf("Create %d threads\n", NTHREADS);
  for(i=0; i<NTHREADS; ++i) {
    rc = pthread_create(&threadid[i], NULL, threadfunc, NULL);
    checkResults("pthread_create()\n", rc);
  }

  sleep(5);  /* Sleep is not a very robust way to serialize threads   */

  for(i=0; i<5; ++i) {
    printf("Wake up a worker, work to do...\n");

    rc = pthread_mutex_lock(&mutex);
    checkResults("pthread_mutex_lock()\n", rc);

    /* In the real world, all the threads might be busy, and        */
    /* we would add work to a queue instead of simply using a flag  */
    /* In that case the boolean predicate might be some boolean     */
    /* statement like: if (the-queue-contains-work)                 */
    if (workToDo) {
       printf("Work already present, likely threads are busy\n");
    }
    workToDo = 1;
    rc = pthread_cond_signal(&cond);
    checkResults("pthread_cond_broadcast()\n", rc);

    rc = pthread_mutex_unlock(&mutex);
    checkResults("pthread_mutex_unlock()\n", rc);
    sleep(5);  /* Sleep is not a very robust way to serialize threads */
  }

  printf("Main completed\n");
  exit(0);
  return 0;
}
Output:

Enter Testcase - QP0WTEST/TPCOS0
Create 2 threads
Thread blocked
Thread blocked
Wake up a worker, work to do...
Thread awake, finish work!
Thread blocked
Wake up a worker, work to do...
Thread awake, finish work!
Thread blocked
Wake up a worker, work to do...
Thread awake, finish work!
Thread blocked
Wake up a worker, work to do...
Thread awake, finish work!
Thread blocked
Wake up a worker, work to do...
Thread awake, finish work!
Thread blocked
Main completed 
1

1 Answers

2
votes

In practical terms, only one thread is awakened and you can't control which one it is.

(pthread_cond_signal wakes up at least one thread waiting on the given condition variable, and the thread chosen is determined by scheduling policy.)

In your case, you need to reconsider what the "condition" represented by your condition variable (condvar) means.

If the condvar truly means "a producer has added an item to one of several queues, each of which has a dedicated consumer," then you should pthread_cond_broadcast to awaken each queue's consumer and let the awakened threads figure out if there is work to do. Alternatively, you might recast the condition as "a producer has added an item to this queue, which has a dedicated consumer," and use one condvar per queue.