pthread_mutex_t ndone_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t ndone_cond = PTHREAD_COND_INITIALIZER;
Поток оповещает главный цикл о своем завершении, увеличивая значение счетчика, пока взаимное исключение принадлежит данному потоку (блокировано им), и используя условную переменную для сигнализации.
Pthread_mutex_lock(ndone_mutex);
ndone++;
Pthread_cond_signal(ndone_cond);
Pthread_mutex_unlock(ndone_mutex);
Затем основной цикл блокируется в вызове функции
pthread_cond_wait
, ожидая оповещения о завершении выполнения потока:while (nlefttoread 0) {
while (nconn maxnconn nlefttoconn 0) {
/* находим файл для чтения */
...
}
/* Ждем завершения выполнения какого-либо потока */
Pthread_mutex_lock(ndone_mutex);
while (ndone == 0)
Pthread_cond_wait(ndone_cond, ndone_mutex);
for (i = 0; i nfiles; i++) {
if (file[i].f_flags F_DONE) {
Pthread_join(file[i].f_tid, (void**)fptr);
/* обновляем file[i] для завершенного потока */
...
}
}
Pthread_mutex_unlock(ndone_mutex);
}
Обратите внимание на то, что переменная
ndone
по-прежнему проверяется, только если потоку принадлежит взаимное исключение. Тогда, если не требуется выполнять какое-либо действие, вызывается функция
pthread_cond_wait
. Таким образом, вызывающий поток переходит в состояние ожидания,
pthread_cond_wait
(после того как поступил сигнал от какого-либо другого потока), он снова блокирует взаимное исключение.Почему взаимное исключение всегда связано с условной переменной? «Условие» обычно представляет собой значение некоторой переменной, используемой совместно несколькими потоками. Взаимное исключение требуется для того, чтобы различные потоки могли задавать и проверять значение условной переменной. Например, если в примере кода, приведенном ранее, отсутствовало бы взаимное исключение, то проверка в главном цикле выглядела бы следующим образом:
/* Ждем завершения выполнения одного или нескольких потоков */
while (ndone == 0)
Pthread_cond_wait(ndone_cond, ndone_mutex);
Но при этом существует вероятность, что последний поток увеличивает значение переменной
ndone
после проверки главным потоком условия
ndone == 0
, но перед вызовом функции
pthread_cond_wait
. Если это происходит, то последний «сигнал» теряется, и основной цикл оказывается заблокированным навсегда, так как он будет ждать события, которое никогда не произойдет.По этой же причине при вызове функции
pthread_cond_wait
поток должен блокировать соответствующее взаимное исключение, после чего эта функция разблокирует взаимное исключение и помещает вызывающий поток в состояние ожидания, выполняя эти действия как одну атомарную операцию. Если бы эта функция не разблокировала взаимное исключение и не заблокировала его снова после своего завершения, то выполнять эти операции пришлось бы потоку, как показано в следующем фрагменте кода:/* Ждем завершения выполнения одного или нескольких потоков */
Pthread_mutex_lock(ndone_mutex);
while (ndone == 0) {
Pthread_mutex_unlock(ndone_mutex);
Pthread_cond_wait(ndone_cond, ndone_mutex);
Pthread_mutex_lock(ndone_mutex);
}
Существует вероятность того, что по завершении выполнения поток увеличит на единицу значение переменной
ndone
и это произойдет между вызовом функций
pthread_mutex_unlock
и
pthread_cond_wait
.Обычно функция
pthread_cond_signal
выводит из состояния ожидания один поток, на который указывает условная переменная. Существуют ситуации, когда некоторый поток знает, что из состояния ожидания должны быть выведены несколько потоков. В таком случае используется функция
pthread_cond_broadcast
, выводящая из состояния ожидания все потоки, которые блокированы условной переменной.#include pthread.h
int pthread_cond_broadcast(pthread_cond_t *