Skip to content

Instantly share code, notes, and snippets.

@rtv
Created February 19, 2013 19:56
Show Gist options
  • Save rtv/4989304 to your computer and use it in GitHub Desktop.
Save rtv/4989304 to your computer and use it in GitHub Desktop.
An example of using pthread's condition variables, showing how to block a main thread while waiting for worker threads to finish their work, without joining the threads. This could be useful if you want to loop the threads, for example.
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
/* Compile like this:
gcc --std=c99 -lpthread cond.c -o cond
*/
const size_t NUMTHREADS = 20;
/* a global count of the number of threads finished working. It will
be protected by mutex and changes to it will be signalled to the
main thread via cond */
int done = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
/* Note: error checking on pthread_X calls ommitted for clarity - you
should always check the return values in real code. */
/* Note: passing the thread id via a void pointer is cheap and easy,
* but the code assumes pointers and long ints are the same size
* (probably 64bits), which is a little hacky. */
void* ThreadEntry( void* id )
{
const int myid = (long)id; // force the pointer to be a 64bit integer
const int workloops = 5;
for( int i=0; i<workloops; i++ )
{
printf( "[thread %d] working (%d/%d)\n", myid, i, workloops );
sleep(1); // simulate doing some costly work
}
// we're going to manipulate done and use the cond, so we need the mutex
pthread_mutex_lock( &mutex );
// increase the count of threads that have finished their work.
done++;
printf( "[thread %d] done is now %d. Signalling cond.\n", myid, done );
// wait up the main thread (if it is sleeping) to test the value of done
pthread_cond_signal( &cond );
pthread_mutex_unlock( & mutex );
return NULL;
}
int main( int argc, char** argv )
{
puts( "[thread main] starting" );
pthread_t threads[NUMTHREADS];
for( int t=0; t<NUMTHREADS; t++ )
pthread_create( &threads[t], NULL, ThreadEntry, (void*)(long)t );
// we're going to test "done" so we need the mutex for safety
pthread_mutex_lock( &mutex );
// are the other threads still busy?
while( done < NUMTHREADS )
{
printf( "[thread main] done is %d which is < %d so waiting on cond\n",
done, (int)NUMTHREADS );
/* block this thread until another thread signals cond. While
blocked, the mutex is released, then re-aquired before this
thread is woken up and the call returns. */
pthread_cond_wait( & cond, & mutex );
puts( "[thread main] wake - cond was signalled." );
/* we go around the loop with the lock held */
}
printf( "[thread main] done == %d so everyone is done\n", (int)NUMTHREADS );
pthread_mutex_unlock( & mutex );
return 0;
}
@KristupasSavickas
Copy link

Nice example, but what's the point of keeping done global? Couldn't you keep it in the loop and increment it in the main loop when a condition occurs? You would need mutexes that way.

@vpatov
Copy link

vpatov commented Nov 23, 2017

@KristupasSavickas the point of done being global is to create a necessity for mutexes. This is a demonstration of thread conditions after all.

@AirborneLizard
Copy link

Should done be volatile?

@driskell
Copy link

Sorry if I misunderstanding. Wouldn’t this deadlock if the main thread entered the loop before the workers finished? It looks like it holds the mutex lock during cond_wait.

@driskell
Copy link

Aha I read it again I misunderstood! :) thanks for the example!

@vineetgarc
Copy link

@driskell, indeed it seems it would deadlock as u mentioned - what am I missing ?

@js0701
Copy link

js0701 commented Feb 18, 2018

It seems will deadlock?

@icthieves
Copy link

@vineetgarc @js0701
pthread_cond_wait() implicitly releases the mutex it is passed when it is called, and implicitly re-aqcuires the mutex when it is returned.
That's why this function takes a condition and a mutex.
So what is happening is that main() acquires the lock, then immediately checks the condition (which hasn't been signaled yet).
Since it hasn't been signaled, pthread_cond_wait() releases the mutex and blocks the main() thread until the condition variable is signaled by another thread.

@o2gy84
Copy link

o2gy84 commented Jan 16, 2019

Is it OK in this example to put pthread_mutex_unlock( & mutex ) before pthread_cond_signal( &cond ); ?

@zyxxel
Copy link

zyxxel commented Apr 28, 2019

No, bad things can happen if you call pthread_cond_signal() without holding the same mutex that waiting threads specifies in their pthread_cond_wait() call.

If pthread_cond_signal() is called without holding the mutex, then the waiting thread can get into an infinite wait because the thread signalling the condition might do it in-between the waiting thread decides if it needs to wait and blocking in pthread_cond_wait().

The pthread_cond_signal() will only wake a waiting thread. If no thread was waiting, then the signal condition was lost and a thread that later starts to wait may wait forever. The above code doesn't suffer from this because inside the mutex-protected critical section, it checks the state of 'done' before deciding if it should wait. With 'done' less than NUMTHREADS, the waiting thread knows that at least one thread have still not signalled - so it's safe to wait on the condition variable.

@ratin3
Copy link

ratin3 commented May 30, 2019

Zyxxel: If you use the same mutex before a pthread_cond_wait() call and pthread_cond_signal(), then they will be mutually exclusive, meaning you won't need to call pthread_cond_wait anyway, just use a flag.

@paul-cybercitizen
Copy link

Is the below message correct? I thought I read that threads can wake from a waiting on a condition for spurious reasons, so we can't assume a signal was received when waking

puts( "[thread main] wake - cond was signalled." );

@paul-cybercitizen
Copy link

Is the below message correct? I thought I read that threads can wake from a waiting on a condition for spurious reasons, so we can't assume a signal was received when waking

puts( "[thread main] wake - cond was signalled." );

The other thread signals before releasing the lock, maybe that guarantees to the waked thread that the signal was received?

@rtv
Copy link
Author

rtv commented Jun 9, 2020

@rtv
Copy link
Author

rtv commented Jun 9, 2020

Note that the condition is checked after cond returns. The worst that can happen is that the puts() is slightly misleading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment