4 } Thread;
5 Thread *tptr; /* массив структур Thread */
6 int listenfd, nthreads;
7 socklen_t addrlen;
8 pthread_mutex_t mlock;
Мы также объявляем несколько глобальных переменных, таких как дескриптор прослушиваемого сокета и взаимное исключение, которые должны совместно использоваться всеми потоками.
В листинге 30.22 показана функция
main
.
Листинг 30.22. Функция main для сервера TCP с предварительным порождением потоков
//server/serv07.c
1 #include "unpthread.h"
2 #include "pthread07.h"
3 pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;
4 int
5 main(int argc, char **argv)
6 {
7 int i;
8 void sig_int(int), thread_make(int);
9 if (argc == 3)
10 listenfd = Tcp_listen(NULL, argv[1], addrlen);
11 else if (argc == 4)
12 listenfd = Tcp_1isten(argv[1], argv[2], addrlen);
13 else
14 err_quit("usage: serv07 [ host ] port# #threads");
15 nthreads = atoi(argv[argc - 1]);
16 tptr = Calloc(nthreads, sizeof(Thread));
17 for (i = 0; i nthreads; i++)
18 thread_make(i); /* завершается только основной поток */
19 Signal(SIGINT, sig_int);
20 for (;;)
21 pause; /* потоки все выполнили */
22 }
Функции
thread_make
и
thread_main
показаны в листинге 30.23.
Листинг 30.23. Функции thread_make и thread_main
//server/pthread07.c
1 #include "unpthread.h"
2 #include "pthread07.h"
3 void
4 thread_make(int i)
5 {
6 void *thread_main(void*);
7 Pthread_create(tptr[i].thread_tid, NULL, thread_main, (void*)i);
8 return; /* завершается основной поток */
9 }
10 void*
11 thread_main(void *arg)
12 {
13 int connfd;
14 void web_child(int);
15 socklen_t clilen;
16 struct sockaddr *cliaddr;
17 cliaddr = Malloc(addrlen);
18 printf("thread %d starting\n", (int)arg);
19 for (;;) {
20 clilen = addrlen;
21 Pthread_mutex_lock(mlock);
22 connfd = Accept(listenfd, cliaddr, clilen);
23 Pthread_mutex_unlock(mlock);
24 tptr[(int)arg].thread_count++;
25 web_child(connfd); /* обработка запроса */
26 Close(connfd);
27 }
28 }
7
Создаются потоки, каждый из которых выполняет функцию
pthread_main
. Единственным аргументом этой функции является порядковый номер потока.
21-23
Функция
thread_main
вызывает функции
pthread_mutex_lock
и
pthread_mutex_unlock
соответственно до и после вызова функции
accept
.
Сравнивая строки 6 и 7 в табл. 30.1, можно заметить, что эта последняя версия нашего сервера быстрее, чем версия с созданием нового потока для каждого клиентского запроса. Этого можно было ожидать, так как в данной версии мы сразу создаем пул потоков и не тратим время на создание новых потоков по мере поступления клиентских запросов. На самом деле эта версия сервера — самая быстродействующая для всех операционных систем, которые мы испытывали.
В табл. 30.2 показано распределение значений счетчика
thread_count
структуры
Thread
, которые мы выводим с помощью обработчика сигнала
SIGINT
по завершении работы сервера. Равномерность этого распределения объясняется тем, что при выборе потока, который будет блокировать взаимное исключение, алгоритм планирования загрузки потоков последовательно перебирает все потоки в цикле.