Во-первых, вы выполняете работу gcd()
, удерживая блокировку ... так что (a) только один поток будет выполнять любую работу одновременно, хотя (b) , который не полностью объясняет, почему только один поток выполняет (почти) всю работу - как говорит KamilCuk, может быть так мало работы, что она (почти) все выполняется до того, как второй поток проснется правильно. [Еще exoti c, может быть некоторая задержка между потоком «a», разблокирующим мьютекс, и запуском другого потока, так что поток «a» может получить мьютекс до того, как туда попадет другой поток.]
POSIX говорит, что когда мьютекс разблокирован, если есть ожидающие, тогда «политика планирования должна определять, какой поток получит мьютекс». «Политика планирования» по умолчанию (насколько мне известно) определена в реализации.
Вы можете попробовать несколько вещей: (1) используйте pthread_barrier_t
для хранения всех потоки в начале thread_function()
, пока все они не будут запущены; (2) используйте sched_yield(void)
после pthread_mutex_unlock()
, чтобы побудить систему запустить новый запускаемый поток.
Во-вторых, вы ни при каких обстоятельствах не должны рассматривать «условную переменную» как сигнал. Чтобы main()
знал, что все потоки завершены, вам нужен счетчик, который может быть pthread_barrier_t
; или это может быть простое целое число, защищенное мьютексом, с «условной переменной» для удержания основного потока, пока он ожидает; или это может быть счетчик (в main()
) и семафор (отправляется один раз каждым потоком при выходе).
В-третьих, вы показываете pthread_cond_wait(&cv, &lock);
в main()
. В этот момент main()
должен владеть lock
... и важно, когда это произошло. Но: в существующем виде, поток первый , который обнаружит list
пустой, запустит cv
, а main()
продолжит работу, даже если другие потоки все еще работают. Хотя однажды main()
действительно повторно получает lock
, все потоки, которые все еще работают, будут либо завершены, либо застрянут на lock
. (Это беспорядок.)
В общем, шаблон для использования «условной переменной»:
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: «условная переменная» не имеет значения ... несмотря на название, это не флаг, указывающий, что какое-то условие истинно. «Условная переменная» - это, по сути, очередь потоков, ожидающих перезапуска. Когда сигнализируется «условная переменная», по крайней мере один ожидающий поток будет перезапущен, но если нет ожидающих потоков, ничего не происходит, в частности (так называемая) «условная переменная» сохраняет нет памяти сигнала.
В новом коде, следуя приведенному выше шаблону, main()
должно:
/* 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);
Итак, что здесь происходит?
main()
ожидает thread_finished_count == 3
thread_finished_count
- это общая переменная, "защищенная" мьютексом thread_lock
.
... поэтому он увеличивается на thread_function()
под мьютексом.
... и main()
также должны читать его под мьютексом.
если main()
находит thread_finished_count != 3
, он должен подождать.
для этого он выполняет: pthread_cond_wait(&thread_cv, &thread_lock)
, что:
и выполняет эти атомарно .
когда thread_function()
выполняет pthread_cond_signal(&thread_cv)
, он пробуждает ожидающий поток.
когда поток main()
просыпается, он сначала повторно получает thread_lock
...
... так что он может затем перейдите к перечитыванию thread_finished_count
, чтобы увидеть, теперь ли это 3
.
FWIW: я рекомендую не уничтожать мьютексы и т. Д. c до после все потоки будут объединены.