44 while (nconn < maxnconn && nlefttoconn > 0) {
45 /* находим файл для считывания */
46 for (i = 0; i < nfiles; i++)
47 if (file[i].f_flags == 0)
48 break;
49 if (i == nfiles)
50 err_quit("nlefttoconn = %d but nothing found", nlefttoconn);
51 file[i].f_flags = F_CONNECTING;
52 Pthread_create(&tid, NULL, &do_get_read, &file[i]);
53 file[i].f_tid = tid;
54 nconn++;
55 nlefttoconn--;
56 }
57 /* Ждем завершения выполнения одного из потоков */
58 Pthread_mutex_lock(&ndone_mutex);
59 while (ndone == 0)
60 Pthread_cond_wait(&ndone_cond, &ndone_mutex);
61 for (i = 0; i < nfiles; i++) {
62 if (file[i].f_flags & F_DONE) {
63 Pthread_join(file[i].f_tid, (void**)&fptr);
64 if (&file[i] != fptr)
65 err_quit("file[i] != fptr");
66 fptr->f_flags = F_JOINED; /* clears F_DONE */
67 ndone--;
68 nconn--;
69 nlefttoread--;
70 printf("thread %d for %s done\n", fptr->f_tid, fptr->f_name);
71 }
72 }
73 Pthread_mutex_unlock(&ndone_mutex);
74 }
75 exit(0);
76 }
44-56
57-60
ndone
станет равно нулю. Как сказано в разделе 26.8, эта проверка должна быть проведена перед тем, как взаимное исключение будет блокировано, а переход потока в состояние ожидания осуществляется функцией pthread_cond_wait
.61-73
file
, отыскивая соответствующий поток, вызываем pthread_join
, а затем устанавливаем новый флаг F_JOINED
.В табл. 16.1 показано, сколько времени требует выполнение этой версии веб-клиента, а также версии, использующей неблокируемую функцию connect
26.10. Резюме
Создание нового потока обычно требует меньше времени, чем порождение нового процесса с помощью функции fork
Все потоки одного процесса совместно используют глобальные переменные и дескрипторы, тем самым эта информация становится доступной всем потокам процесса. Но совместное использование информации вносит проблемы, связанные с синхронизацией доступа к разделяемым переменным, и поэтому нам следует использовать примитивы синхронизации технологии Pthreads — взаимные исключения и условные переменные. Синхронизация доступа к совместно используемым данным — необходимое условие почти для любого приложения, работающего с потоками.
При разработке функций, которые могут быть вызваны таким приложением, нужно учитывать требование безопасности в многопоточной среде. Это требование выполнимо при использовании собственных данных потоков (thread-specific data), пример которых мы показали при рассмотрении функции readline
К модели потоков мы вернемся в главе 30, где сервер при запуске создает пул потоков. Для обслуживания очередного клиентского запроса используется любой свободный поток.
Упражнения
1. Сравните использование дескриптора в случае, когда в коде сервера применяется функция fork
2. Что произойдет в листинге 26.2, если поток при завершении функции str_echo
close
для закрытия сокета?3. В листингах 5.4 и 6.2 мы выводили сообщение Server terminated prematurely