First, you are doing the gcd()
work while holding the lock... so (a) only one thread will do any work at any one time, though (b) that does not entirely explain why only one thread appears to do (nearly) all the work -- as KamilCuk says, it may be that there is so little work to do, that it's (nearly) all done before the second thread wakes up properly. [More exotic, there may be some latency between thread 'a' unlocking the mutex and another thread starting to run, such that thread 'a' can acquire the mutex before another thread gets there.]
POSIX says that when a mutex is unlocked, if there are waiters then "the scheduling policy shall determine which thread shall acquire the mutex". The default "scheduling policy" is (to the best of my knowledge) implementation defined.
You could try a couple of things: (1) use a pthread_barrier_t
to hold all the threads at the start of thread_function()
until they are all running; (2) use sched_yield(void)
after pthread_mutex_unlock()
to prompt the system into running the newly runnable thread.
Second, you should not under any circumstance treat a 'condition variable' as a signal. For main()
to know that all threads have finished you need a count -- which could be a pthread_barrier_t
; or it could be simple integer, protected by a mutex, with a 'condition variable' to hold the main thread on while it waits; or it could be a count (in main()
) and a semaphore (posted once by each thread as it exits).
Third, you show pthread_cond_wait(&cv, &lock);
in main()
. At that point main()
must own lock
... and it matters when that happened. But: as it stands, the first thread to find the list
empty will kick cv
, and main()
will proceed, even though other threads are still running. Though once main()
does re-acquire lock
, any threads which are then still running will either be exiting or will be stuck on the lock
. (It's a mess.)
In general, the template for using a 'condition variable' is:
pthread_mutex_lock(&...lock) ; while (!(... thing we need ...)) pthread_cond_wait(&...cond_var, &...lock) ; ... do stuff now we have what we need .... pthread_mutex_unlock(&...lock) ;
NB: a 'condition variable' does not have a value... despite the name, it is not a flag to signal that some condition is true. A 'condition variable' is, essentially, a queue of threads waiting to be re-started. When a 'condition variable' is signaled, at least one waiting thread will be re-started -- but if there are no threads waiting, nothing happens, in particular the (so called) 'condition variable' retains no memory of the signal.
In the new code, following the above template, main()
should:
/* wait for threads .... */ status = pthread_mutex_lock(&thread_lock); chcek_status(status); while (thread_finished_count != 3) { pthread_cond_wait(&thread_cv, &thread_lock) ; chcek_status(status); } ; status = pthread_mutex_unlock(&thread_lock) ; chcek_status(status);
So what is going on here ?
main()
is waiting forthread_finished_count == 3
thread_finished_count
is a shared variable "protected" by thethread_lock
mutex....so it is incremented in
thread_function()
under the mutex....and
main()
must also read it under the mutex.if
main()
findsthread_finished_count != 3
it must wait.to do that it does:
pthread_cond_wait(&thread_cv, &thread_lock)
, which:unlocks
thread_lock
places the thread on the
thread_cv
queue of waiting threads.
and it does those atomically.
when
thread_function()
does thepthread_cond_signal(&thread_cv)
it wakes up the waiting thread.when the
main()
thread wakes up, it will first re-acquire thethread_lock
......so it can then proceed to re-read
thread_finished_count
, to see if it is now3
.
FWIW: I recommend not destroying the mutexes etc until after all the threads have been joined.