UNIX: разработка сетевых приложений, стр. 292
Некоторые системы (например, Solaris) определяют константу PTHREAD_MUTEX_INITIALIZER как 0. Если данная инициализация будет опущена, это ни на что не повлияет, так как статически размещаемые переменные все равно автоматически инициализируются нулем. Но для других систем такой гарантии дать нельзя — например, в Digital Unix константа инициализации ненулевая.
В листинге 26.12 приведена исправленная версия листинга 26.11, в которой используется одно взаимное исключение для блокирования счетчика при работе с двумя потоками.
Листинг 26.12. Исправленная версия листинга 26.11, использующая взаимное исключение для защиты совместно используемой переменной
//threads/examplе01.с 1 #include "unpthread.h" 2 #define NLOOP 5000 3 int counter; /* увеличивается потоками */ 4 pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER; 5 void *doit(void*); 6 int 7 main(int argc, char **argv) 8 { 9 pthread_t tidA, tidB;10 Pthread_create(&tidA, NULL, &doit, NULL);11 Pthread_create(&tidB, NULL, &doit, NULL);12 /* ожидание завершения обоих потоков */13 Pthread_join(tidA, NULL);14 Pthread_join(tidB, NULL);15 exit(0);16 }17 void*18 doit(void *vptr)19 {20 int i, val;21 /*22 * Каждый поток считывает, выводит и увеличивает счетчик NLOOP раз.23 * Значение счетчика должно возрастать монотонно.24 */25 for (i = 0; i < NLOOP; i++) {26 Pthread_mutex_lock(&counter_mutex);27 val = counter;28 printf(%d: %d\n", pthread_self(), val + 1);29 counter = val + 1;30 Pthread_mutex_unlock(&counter_mutex);31 }32 return(NULL);33 }Мы объявляем взаимное исключение с именем
counter_mutexНасколько серьезной является дополнительная нагрузка, связанная с использованием взаимных исключений? Мы изменили программы, приведенные в листингах 26.11 и 26.12, заменив значение
NLOOP/dev/null26.8. Условные переменные
Взаимное исключение позволяет предотвратить одновременный доступ к совместно используемой (разделяемой) переменной, но для того чтобы перевести поток в состояние ожидания (спящее состояние) до момента выполнения некоторого условия, необходим другой механизм. Продемонстрируем сказанное на следующем примере. Вернемся к нашему веб-клиенту из раздела 26.6 и заменим функцию Solaris
thr_joinpthread_joinpthread_joinint ndone; /* количество потоков, завершивших выполнение */pthread_mutex_t ndone_mutex = PTHREAD_MUTEX_INITIALIZER;Затем мы требуем, чтобы каждый поток по завершении своего выполнения увеличивал этот счетчик на единицу, используя соответствующее взаимное исключение.
void* do_get_read(void *vptr) { ... Pthread_mutex_lock(&ndone_mutex); ndone++; Pthread_mutex_unlock(&ndone_mutex); return(fptr); /* завершение выполнения потока */}Но каким при этом получается основной цикл? Взаимное исключение должно быть постоянно блокировано основным циклом, который проверяет, какие потоки завершили свое выполнение.
while (nlefttoread > 0) { while (nconn < maxnconn && nlefttoconn > 0) { /* находим файл для чтения */ ... } /* Проверяем, не завершен ли поток */ Pthread_mutex_lock(&ndone_mutex); if (ndone > 0) { for (i =0; i < nfiles; i++) { if (file[i].f_flags & F_DONE) { Pthread_join(file[i].f_tid, (void**)&fptr); /* обновляем file[i] для завершенного потока */ ... } }