-
-
Save rtv/4989304 to your computer and use it in GitHub Desktop.
#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; | |
} |
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.
Aha I read it again I misunderstood! :) thanks for the example!
@driskell, indeed it seems it would deadlock as u mentioned - what am I missing ?
It seems will deadlock?
@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.
Is it OK in this example to put pthread_mutex_unlock( & mutex )
before pthread_cond_signal( &cond );
?
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.
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.
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." );
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?
Note that the condition is checked after cond returns. The worst that can happen is that the puts() is slightly misleading.
Should
done
be volatile?