Автоматическое управление потоками сервера
Автоматическое управление потоками сервера
Чтобы посмотреть, как осуществляется управление потоками сервера, добавим в процедуру сервера команду выдачи ее идентификатора потока. Добавим в нее также пятисекундную паузу, чтобы имитировать длительное выполнение. За это время мы сможем запустить несколько клиентов. В листинге 15.6 приведен текст новой процедуры сервера.
Листинг 15.6. Процедура сервера, выводящая идентификатор потока
//doors/server5.c
1 #include "unpipc.h"
2 void
3 servproc(void *cookie, char *dataptr, size_t datasize,
4 door_desc_t *descptr, size_t ndesc)
5 {
6 long arg, result;
7 arg = *((long *) dataptr);
8 printf("thread id %ld, arg = %ld ", pr_thread_id(NULL), arg);
9 sleep(5);
10 result = arg * arg;
11 Door_return((char*)&result, sizeof(result), NULL, 0);
12 }
Здесь используется новая функция из нашей библиотеки — pr_thread_id. Она принимает один аргумент (указатель на идентификатор потока или нулевой указатель вместо идентификатора вызвавшего потока) и возвращает идентификатор этого потока (обычно небольшое целое число, но всегда в формате длинного целого). Процессу всегда можно сопоставить целое число — его идентификатор. Хотя мы и не знаем, к какому типу принадлежит идентификатор процесса (int или long), мы просто преобразуем значение, возвращаемое getpid, к типу long и выводим значение (листинг 9.2). Однако идентификатор потока принадлежит к типу pthread_t, который не обязательно является одним из целых типов. И действительно, в Solaris 2.6 идентификаторами потоков являются короткие целые, тогда как в Digital Unix используются указатели. Однако часто возникает необходимость сопоставлять потокам небольшие целые числа для задач отладки (как в данном примере). Наша библиотечная функция, текст которой приведен в листинге 15.7, решает этот вопрос.
Листинг 15.7. Функция pr_thread_id: возвращает небольшой целочисленный идентификатор потока
//lib/wrappthread.c
245 long
246 pr_thread_id(pthread_t *ptr)
247 {
248 #if defined(sun)
249 return((ptr == NULL) ? pthread_self() : *ptr); /* Solaris */
250 #elif defined(__osf__) && defined(__alpha)
251 pthread_t tid;
252 tid = (ptr == NULL) ? pthread_self() : *ptr; /* Digital Unix */
253 return(pthread_getsequence_np(tid));
254 #else
255 /* прочие системы */
256 return((ptr == NULL) ? pthread_self() : *ptr);
257 #endif
258 }
Если в данной реализации идентификатор потока не является небольшим целым числом, функция может быть сложнее. Она может осуществлять отображение значений типа pthread_t в целые числа и сохранять эти отображения для последующих вызовов в массиве или связном списке. Эта задача решена в функции thread_name в книге [13].
Вернемся к программе из листинга 15.6. Запустим ее три раза подряд. Поскольку нам приходится ждать возвращения подсказки интерпретатора, чтобы запустить клиент еще раз, мы можем быть уверены, что каждый раз выполняется пятисекундная пауза:
solaris % client5 /tmp/server5 55
result: 3025
solaris % client5 /tmp/server5 66
result: 4356
solaris % client5 /tmp/server5 77
result: 5929
Взглянув на текст, выводимый сервером, мы увидим, что клиенты каждый раз обслуживались одним и тем же потоком сервера:
solaris % server5 /tmp/server5
thread id 4, arg = 55
thread id 4, arg = 66
thread id 4, arg = 77
Теперь запустим три экземпляра программы-клиента одновременно:
solaris % client5 /tmp/server5 11 & client5 /tmp/server5 22 & client5 /tmp/server5 33 &
[2] 3812
[3] 3813
[4] 3814
solaris % result: 484
result: 121
result: 1089
Выводимый сервером текст показывает, что для обработки второго и третьего вызова процедуры сервера создаются новые потоки:
thread id 4, arg = 22
thread id 5, arg = 11
thread id 6, arg = 33
Затем мы запустим еще два клиента одновременно (первые три уже завершили работу):
solaris % client5 /tmp/server5 11 & client5 /tmp/server5 22 &
[2] 3830
[3] 3831
solaris % result: 484
result: 121
При этом сервер использует созданные ранее потоки:
thread id 6, arg = 22
thread id 5, arg = 11
Этот пример показывает, что серверный процесс (то есть библиотека дверей, подключенная к нему) автоматически создает потоки серверных процедур по мере необходимости. Если приложению требуется контроль над созданием потоков, оно может его осуществить с помощью функций, описанных в разделе 15.9.
Мы также убедились, что сервер в этом случае является параллельным (concurrent): одновременно может выполняться несколько экземпляров процедуры сервера в виде отдельных потоков для обслуживания клиентов. Это следует также из того, что результат работы сервера выводится тремя экземплярами клиента одновременно пять секунд спустя после их одновременного запуска. Если бы сервер был последовательным, первый результат появился бы через 5 секунд после запуска, следующий — через 10, а последний — через 15.
Данный текст является ознакомительным фрагментом.