UNIX: разработка сетевых приложений
UNIX: разработка сетевых приложений читать книгу онлайн
Новое издание книги, посвященной созданию веб-серверов, клиент-серверных приложений или любого другого сетевого программного обеспечения в операционной системе UNIX, — классическое руководство по сетевым программным интерфейсам, в частности сокетам. Оно основано на трудах Уильяма Стивенса и полностью переработано и обновлено двумя ведущими экспертами по сетевому программированию. В книгу включено описание ключевых современных стандартов, реализаций и методов, она содержит большое количество иллюстрирующих примеров и может использоваться как учебник по программированию в сетях, так и в качестве справочника для опытных программистов.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
53-54thr_joinpthread_joinПоказанное здесь решение, в котором используется функция потоков thr_join Solaris, не является, вообще говоря, совместимым со всеми системами. Тем не менее мы приводим здесь эту версию веб-клиента, использующую потоки, чтобы не осложнять обсуждение рассмотрением условных переменных и взаимных исключений (mutex). К счастью, в Solaris допустимо смешивать потоки Pthreads и потоки Solaris.
В листинге 26.9 показана функция
do_get_readGETЛистинг 26.9. Функция do_get_read
//threads/web01.c61 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-71tcp_connectconnect72write_get_cmdGETFD_SETmaxfd73-82F_DONEМы также не показываем функцию
home_pageМы вернемся к этому примеру, заменив функцию Solaris
thr_join26.7. Взаимные исключения
Обратите внимание на то, что в листинге 26.8 при завершении выполнения очередного потока в главном цикле уменьшаются на единицу и
nconnnlefttoreaddo_get_readПроблема, возникающая при помещении определенного кода в функцию, которая выполняется каждым потоком, заключается в том, что обе эти переменные являются глобальными, а не собственными переменными потока. Если один поток в данный момент уменьшает значение переменной и это действие приостанавливается, чтобы выполнился другой поток, который также станет уменьшать на единицу эту переменную, может произойти ошибка. Предположим, например, что компилятор С осуществляет уменьшение переменной на единицу в три этапа: загружает информацию из памяти в регистр, уменьшает значение регистра, а затем сохраняет значение регистра в памяти. Рассмотрим возможный сценарий.
1. Выполняется поток А, который загружает в регистр значение переменной
nconn2. Система переключается с выполнения потока А на выполнение потока В. Регистры потока А сохранены, регистры потока В восстановлены.
3. Поток В выполняет три действия, составляющие оператор декремента в языке С (
nconn--nconn4. Впоследствии в некоторый момент времени система переключается на выполнение потока А. Восстанавливаются регистры потока А, и он продолжает выполняться с того места, на котором остановился, а именно начиная со второго этапа из трех, составляющих оператор декремента. Значение регистра уменьшается с 3 до 2, и значение 2 записывается в переменную
nconnОкончательный результат таков: значение
nconnПодобные ошибки параллельного программирования трудно обнаружить по многим причинам. Во-первых, они возникают нечасто. Тем не менее это ошибки, которые по закону Мэрфи вызывают сбои в работе программ. Во-вторых, ошибки такого типа возникают не систематически, так как зависят от недетерминированного совпадения нескольких событий. Наконец, в некоторых системах аппаратные команды могут быть атомарными. Это значит, что имеется аппаратная команда уменьшения значения целого числа на единицу (вместо трехступенчатой последовательности, которую мы предположили выше), а аппаратная команда не может быть прервана до окончания своего выполнения. Но это не гарантировано для всех систем, так что код может работать в одной системе и не работать в другой.
