UNIX: разработка сетевых приложений
ПРИМЕЧАНИЕНекоторые системы (например, 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 50003 int counter; /* увеличивается потоками */4 pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;5 void *doit(void*);6 int7 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. Когда мы запускали эту программу, результат всегда был правильным: значение переменной увеличивалось монотонно, а ее окончательное значение всегда оказывалось равным 10 000.counter_mutexНасколько серьезной является дополнительная нагрузка, связанная с использованием взаимных исключений? Мы изменили программы, приведенные в листингах 26.11 и 26.12, заменив значение
на 50 000 (вместо исходного значения 5000), и засекли время, направив вывод на устройствоNLOOP. Время работы центрального процессора в случае корректной версии, использующей взаимное исключение, увеличилось относительно времени работы некорректной версии без взаимного исключения на 10 %. Это означает, что использование взаимного исключения не связано со значительными издержками./dev/null26.8. Условные переменные
Взаимное исключение позволяет предотвратить одновременный доступ к совместно используемой (разделяемой) переменной, но для того чтобы перевести поток в состояние ожидания (спящее состояние) до момента выполнения некоторого условия, необходим другой механизм. Продемонстрируем сказанное на следующем примере. Вернемся к нашему веб-клиенту из раздела 26.6 и заменим функцию Solaris
наthr_join. Но мы не можем вызвать функциюpthread_joinдо тех пор, пока не будем знать, что выполнение потока завершилось. Сначала мы объявляем глобальную переменную, которая служит счетчиком количества завершившихся потоков, и организуем управление доступом к ней с помощью взаимного исключения.pthread_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] для завершенного потока */...}}