UNIX: разработка сетевых приложений
UNIX: разработка сетевых приложений читать книгу онлайн
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
53-54
thr_join
pthread_join
Показанное здесь решение, в котором используется функция потоков thr_join Solaris, не является, вообще говоря, совместимым со всеми системами. Тем не менее мы приводим здесь эту версию веб-клиента, использующую потоки, чтобы не осложнять обсуждение рассмотрением условных переменных и взаимных исключений (mutex). К счастью, в Solaris допустимо смешивать потоки Pthreads и потоки Solaris.
В листинге 26.9 показана функция
do_get_read
GET
Листинг 26.9. Функция do_get_read
//threads/web01.c
61 void*
62 do_get_read(void *vptr)
63 {
64 int fd, n;
65 char line[MAXLINE];
66 struct file *fptr;
67 fptr = (struct file*)vptr;
68 fd = Tcp_connect(fptr->f_host, SERV);
69 fptr->f_fd = fd;
70 printf("do_get_read for %s, fd %d, thread %dn",
71 fptr->f_name, fd, fptr->f_tid);
72 write_get_cmd(fptr);
73 /* Чтение ответа сервера */
74 for (;;) {
75 if ((n = Read(fd, line, MAXLINE)) == 0)
76 break; /* сервер закрывает соединение */
77 printf ("read %d bytes from %sn", n, fptr->f_name);
78 }
79 printf("end-of-file on %sn", fptr->f_name);
80 Close(fd);
81 fptr->f_flags = F_DONE; /* сбрасываем F_READING */
82 return (fptr); /* завершение потока */
83 }
68-71
tcp_connect
connect
72
write_get_cmd
GET
FD_SET
maxfd
73-82
F_DONE
Мы также не показываем функцию
home_page
Мы вернемся к этому примеру, заменив функцию Solaris
thr_join
26.7. Взаимные исключения
Обратите внимание на то, что в листинге 26.8 при завершении выполнения очередного потока в главном цикле уменьшаются на единицу и
nconn
nlefttoread
do_get_read
Проблема, возникающая при помещении определенного кода в функцию, которая выполняется каждым потоком, заключается в том, что обе эти переменные являются глобальными, а не собственными переменными потока. Если один поток в данный момент уменьшает значение переменной и это действие приостанавливается, чтобы выполнился другой поток, который также станет уменьшать на единицу эту переменную, может произойти ошибка. Предположим, например, что компилятор С осуществляет уменьшение переменной на единицу в три этапа: загружает информацию из памяти в регистр, уменьшает значение регистра, а затем сохраняет значение регистра в памяти. Рассмотрим возможный сценарий.
1. Выполняется поток А, который загружает в регистр значение переменной
nconn
2. Система переключается с выполнения потока А на выполнение потока В. Регистры потока А сохранены, регистры потока В восстановлены.
3. Поток В выполняет три действия, составляющие оператор декремента в языке С (
nconn--
nconn
4. Впоследствии в некоторый момент времени система переключается на выполнение потока А. Восстанавливаются регистры потока А, и он продолжает выполняться с того места, на котором остановился, а именно начиная со второго этапа из трех, составляющих оператор декремента. Значение регистра уменьшается с 3 до 2, и значение 2 записывается в переменную
nconn
Окончательный результат таков: значение
nconn
Подобные ошибки параллельного программирования трудно обнаружить по многим причинам. Во-первых, они возникают нечасто. Тем не менее это ошибки, которые по закону Мэрфи вызывают сбои в работе программ. Во-вторых, ошибки такого типа возникают не систематически, так как зависят от недетерминированного совпадения нескольких событий. Наконец, в некоторых системах аппаратные команды могут быть атомарными. Это значит, что имеется аппаратная команда уменьшения значения целого числа на единицу (вместо трехступенчатой последовательности, которую мы предположили выше), а аппаратная команда не может быть прервана до окончания своего выполнения. Но это не гарантировано для всех систем, так что код может работать в одной системе и не работать в другой.