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