Ю. Вахалия - UNIX изнутри (2003) (1114670), страница 72
Текст из файла (страница 72)
В качестве примера можно представить ситуацию, при которой сообщение приходит уже после проведения проверки очереди, но до того момента, как нить перешла в режим ожидания. В таком случае нить окажется приостановленной даже при наличии необработанных сообщений. Следова. тельно, для защиты от подобных проблем необходимо использовать неделимую операцию проверки условия и блокировки нити. Такая возможность достигается при помощи дополнительных элементов блокировки. Объекты блокировки (обычно это простые блокировки или зр(л 1осЬ) защищают совместно используемые данные и предохраняют их от возникновения ситуации «невыхода» из режима ожидания.
Для этого нить серверного приложения создает объект блокировки над о зсредью сообщений и проверяет очередь на отсутствие запросов. Если очередь окажется пустой, нить вызовет функцию вяа(1О с условием, по которому происходит удержание блокировки. Объект блокировки является входным аргументом функции »тай(). Функция производит приостановление выполнения нити и освобождение объекта блокировки. После получения сообщения и возобновления выполнения нити вызов»уаИ~) снова запрашивает объект блокировки, после чего ее работа завершается. Пример использования условных переменных показан в листинге 7.8.
Листинг 7.8. Применение условных переменных зтгосс сопп1В1оп ( ргос *пека, /* двунаправленный связанный список */ ргос *ргев: /* приостановленных нитей */ зр1п1оск » 1озс1.оск: /* используется для защиты списка */ 7.7. Условные переменные 309 чо!О ыа15 (сопбтт!Оп *с, 50!п10ск т *5) ( 50!и 10сХ(ас.>115тсосХ): або ве1т го тпе 1!пйеб 1тзт: 5Ртп ил10СХ(ас- 1!55~0СХ): зрщ ип1осХ(5); /* освобождение обьекта блокировки до приостановки выполнения нити *! зы(СЬО: /* переключение контекста */ зртп 10СХ(5); /* повторный запрос объекта блокировки *! гетигл; ЧО!О 00 5!9ла1(соп01510п *С) /* пробуждение одной нити, находящейся в ожидании по зтону условию *! зр!и 10СХ(ас- 1!55(.оси): удаление одной нити из связанного списка, если тот не пуст, 501п 0010ск(ас->1!55(.ОсХ); если нить удалена из списка.
делаеи ее работоспособной; геъогп; чо!о оо ьгоаоса51(соло!с!оп *с) !* пробуждение всех нитей, находящихся в режиие ожидания по заданноиу условию *! 5ртп 10сХ(ас->115тсОсХ): но!1е (связанный список не пуст) ( удаление нити из связанного списка и восстановление работоспособности нити 50!и Оп!оси(ас->1!55СОсд); ) 7.7.1. Некоторые детали реализации условных переменных При рассмотрении механизма условных переменных необходимо сделать несколько важных замечаний.
Логическое условие само по себе не является частью переменной, таким образом, оно должно проверяться до вызова тча)тО. Более того, необходимо помнить о том, что при реализации механизма применяются сразу два объекта блокировки. Одним из этих объектов является ()зт)юск, который используется для защиты списка нитей, находящихся в ожидании изменения условия. Второй обьект защищает проверяемые данные. Он не 310 Глава 7.
Синхронизация. Многопроцессорные системы является частью условной переменной, но передается в качестве аргумента функции вай(). Вызов ьитспО и участок кода, меняющий режим ожидающей нити на работоспособный, может использовать для защиты очередей планировщика еще один объект блокировки. Таким образом, возникает ситуация, при которой нить, пытающаяся получить один объект блокировки, уже удерживает еще один объект такого же типа. Это не представляет опасности, так как объекты блокировки имеют всего лишь одно ограничение: они запрещают приостановку выполнения нити, удерживаюгцей один из таких обьектов. Защита от зависания гарантирована при строго определенном порядке следования обьектов; блокировка по условию должна запрашиваться до запроса Бьт(.оск.
Очередь ожидающих нитей не обязательно должна быть частью структуры условия. Вместо этого можно применять глобальный набор каналов сна, точно такой же, как в традиционных системах ПЧ1Х. В этом случае объект бы(.оск в условии заменяется обьектом блокировки, защигцающим соответствующий канал спа. Оба приведенных метода обладают определенными достоинствами, о которых мы уже рассказывали ранее. Одним из достоинств условных переменных является существование двух различных методов обработки события. После его возникновения можно возобновить работу либо одной нити (при помощи г(о з1дпа(()), либо всех ожидающих нитей сразу (используя команду до бгоаосаьг()). Каждый из этих вариантов может оказаться наиболее подходящим в той или иной ситуации.
В случае применения механизма условных переменных серверными приложениями эффективнее всего возобновлять работу только одной нити, так как каждый запрос обычно обрабатывается единственной нитью. Однако существуют и иные ситуации, например, когда программа выполняется сразу же несколькими нитями, использующими совместно одну копию исходных кодов программы.
Если код программы, не хранящийся постоянно в памяти, попытаются запросить сразу несколько нитей, результатом станет получение ошибки каждой из них. Первая нить инициирует доступ к странице, расположенной на лиске. Остальные нити получают уведомление о начале операции чтения данных и приостановят выполнение в ожидании завершения ввода-вывода.
Когда страница кода будет считана в память, наилучшим решением является вызов оо бгоаосаьт() и возобновление работы всех приостановленных нитей, после чего все они могут иметь доступ к этой странице без возникновения ошибок. 7.7.2. События Чаще всего логическое условие переменной является простым. Обычно нить ожидает завершения выполнения определенной задачи. Момент завершения легко обозначить при помощи установки глобального флага, Ситуацию можно описать более приемлемым способом при помощи элемента высшего уров- 7.8. Синхронизация чтения-записи 31 1 вя, называемого событием (ечеп!). Событие включает в себя флаг доре, объект блокировки, защищающий этот флаг, и условную переменную на единый объект. Событием легко манипулировать при помощи двух простых операций — ааа)е0опе() и вет0опе().
Команда аееай0опео приведет к блокировке, установленной до тех пор, пока не произойдет определенное событие, а операция ве10опео помечает событие ечеп! как уже свершившееся и возобновляет выполнение всех нитей, заблокированных в ожидании этого события. Помимо этих команд интерфейс взаимодействия с объектами-событиями может поддерживать неблокирующие функции тевт0опео и геветО, которые снова помечают событие ечеп! как сше не свершившееся.
В некоторых случаях флаг гуопе можно заменить переменной, что позволит передавать более подробну1о информацию при наступлении события. 7.7.3. Блокирующие объекты Часто возникают ситуации, при которых нити нужно удерживать ресурс на продолжительный период времени, в течение которого объект должен иметь возможность блокировки по другим событиям. При этом нить, которой необходим ресурс, не начинает выполнять цикл ожидания его освобождения, а приостанавливается. Для реализации такого подхода следует использовать элемент, называемый блокирующим объектом синхронизации (Ыос!е!пя !ос!е).
Такой объект поддерживает две основные операции, (ос!еО и оп(осЦ), а также дополнительную команду т~у!.ос!е(). Элемент синхронизирует два объекта— флаг (ос!еед, относящийся к ресурсу, и очередь сна. Неделимость операций гарантирует дополнительное использование объектов простой блокировки. Такие объекты могут быть реализованы при помощи обычных условных переменных, где проверяемым выражением является сброс флага (острее!. С точки зрения производительности объекты блокируюшсй синхронизации лучше всего реализовывать как базовые элементы.
В частности, если каждый ресурс обладает собственным каналом сна, то для защиты флага и канала можно применять единственный объект блокировки. 7.8. Синхронизация чтения-записи Изменение ресурса требует установки эксклюзивного права на доступ к нему.
Это означает, что его модификацию может производить в один момент времени только одна нить. Однако в большинстве случаев целесообразно разрешить сразу нескольким нитям читать совместно используемые данные в течение промежутка времени, пока никто не попытается произвести запись информации, Для реализации такого подхода требуется сложный механизм синхронизации, поддерживающий одновременно два типа доступа к ресурсу, эксклюзивный и совместный.