Р.У. Себеста - Основные копцепции языков программирования (2001) (1160794), страница 134
Текст из файла (страница 134)
В данном случае совместно используемый буфер хранит целые числа н имеет круговую логическую структуру. Он разработан для возможного использования несколькмми задачами типа "производитель- потребитель". Приведенный ниже код представляет собой определение залач типа "производитель- потребитель". Для предотвращения потери значимости и переполнения буфера используются два семафора, которые обеспечивают синхронизацию взаимодействия. Предположим, что буфер имеет размер ВОГЕЗ, а подпрограммы, которые с ннм действительно работают,— это описанные выше подпрограммы ГЕТСВ и ОЕРОЯ1т.
Обращения к счетчику семафора обозначиотся точкой. Например, если йи11зрогв — это семафор, то к его счетчику можно обратиться с помощью выражения йц11зрогз. соипг. вещарпоге Йц11зросз, ешртузротзГ 1и11зросз.соцпс := ОГ ешртувросз.соцпс := ВОГЬЕИ; пав)г ргог(цсегг 1оор -- вычислить значение ЧАЕУЕ-- 12.3. Семафоры найт(еирсуяросз]; ( ждать появления свободного места ) РЕРОЯ1Т(ЧАДРЕ)г ге1еазе(1и11зрогз)) ( увеличить количество заполненных ячеек епб 1оорг епс( ргос(цсегг Еав)с сспзишег> 1оор на1г(1ц11зрогз)г ( убедиться, что буфер не пуст РЕТОН(ЧАЕРЕ)) ге1еазе(епрсузросз)) ( увеличить количество пустых ячеек получить значение ЧА)ЛЕ-- епй 1оор епс( сопзшпег) Если буфер в данный момент пуст, семафор йи11зрогя ставит задачу сопзцвег в очередь ожидать, пока в буфере не появится заполненная ячейка.
Если буфер в данный момент полон, семафор ешрсузрогз ставит задачу ргос(исег в очередь ожидать, пока в буфере не появится свободное место. 12.3.3. Синхронизацив конкурвнции Описанный выше буфер не обеспечивает синхронизации конкуренции. Доступом к структуре можно управлять с помощью дополнительного семафора. Этот семафор не должен ничего подсчитывать. Он просто отмечает с помощью своего счетчика, используется ли буфер в данный момент.
Оператор надг открывает доступ к буферу, только если счетчик семафора равен 1, т,е. когда совместно используемый буфер в данный момент не находится в распоряжении ни одной задачи. Если счетчик семафора равен О, т.е. в данный момент буфер кем-то используется, задача помещается в очередь семафора. Заметим, что счетчик семафора должен быть инициализировал единицей. Очередь семафора всегда инициализируется пустой.
Семафор, использующий бинарный счетчик, такой, например, какой мы используем для синхронизации конкуренции, называется бинарным семафором (Ь(па~у зещарйоге). Приведенный ниже пример кода иллюстрирует применение семафора для обеспечения как синхронизации конкуренции, так и синхронизации взаимодействия при параллельном доступе к совместно используемому буферу. Семафор ассезз используется для обеспечения взаимно исключающего доступа к буферу.
Снова заметим, что могут существовать несколько производителей и потребителей. вееарЪоге ассезз, 1ц11зрогз, ежргузрогаг ассезз.соцпс : 1; 1и11зроге.ссцпг := Ог еир узрссз.ссипс := ВУРЕЕИг Сев)с ргос(исегг 1оор -- произвести значение ЧА1ЛЕ-- надс(епрсуяроса)г ( ожидать появления свободного места 514 Глава 1 2. Параллельность иалп(ассеяв); 0ЕРОЯ1Т(ЧАЕ()Е)г ге1еаяе(ассевв); ( освободить доступ ) ге1еаве(йи11врогв)> ( увеличить количество заполненных мест ) епс( 1оор) епс( ргос)исег; пав)с сопвшпег; 1оор иайг(йи11вросв); иа1г (ассевв); ГЕТСН(ЧАЕ(ЗЕ)з ге1еазе(ассевв)) ге1еаяе(еарпувропв)г -- получить значение еп6 1оор епб сопвшпег; ( убедиться, что буфер не пуст ( ожидать доступ ( освободить доступ ) ( увеличить количество пустых мест ) ЧА1Л) Е Даже беглого взгляла на этот пример достаточно, чтобы убедиться в наличии некоторой проблемы.
А именно, предположим, что, пока задача ожидает вызова иалп (ассе я в ) в задаче сопвшпег, другая задача считывает последнее значение из совместно используемого буфера. К счастью, этого случиться не может, поскольку вызов иа(г (ассевв) резервирует значение в буфере для залачи, содержащей этот вызов, с помощью увеличения счетчика семафора Ец11вропв. Есть один важный аспект, связанный с семафорами, который мы еше не обсуждали. Напомним предыдущее описание проблемы синхронизации конкуренции. Операции над совместно используемыми данными не должны прерываться. Если вторая операция может начинаться в то время, когда первая операция все еше выполняется, то совместно используемые данные могут быть повреждены. Семафоры сами по себе являются совместно используемыми объектами, так что операции нал семафорами могут порожлать ту же проблему.
Следовательно, важно. чтобы операции над семафорами нельзя было прерывать. Многие компьютеры имеют команды (выполнение которых нельзя прерывать), разработанные специально для операций над семафорами. Если таких команд нет, то использование семафоров лля синхронизации конкуренции становится серьезной проблемой, не имеющей простого решения. Язык Р(Л был первым языком программирования, допускающим параллельное выполнение задач. Он позволял программам пользователя выполнять любую подпрограмму параллельно с вызывающим ее модулем. Механизм синхронизации этих параллельных вычислений был. однако, совершенно неадекватным.
Он состоял только из бинарных семафоров, называемых событиями (ечепгв), и был способен определить момент, когда задача закончила свое выполнение. Язык А1.0ОЕ 68, допускавший параллельность на уровне составных операторов, содержал тип данных лля семафоров под названием вава. 12.3.4. Оценка 12.3. Семафоры Применение семафоров для синхронизации взаимодействия создает небезопасную среду программирования. Не существует способа статической проверки правильности их применения, зависящего от семантики программы, в которой они появляются.
Так, если в примере с буфером пропустить оператор иагс (ежрсувротв) в ЗадаЧе ргос(цсег, зто может привести к переполнению буфера. Если в этом примере пропустить оператор на(г ( йи11в рог в ) в задаче соп веже г, зто может привести к потере значимости.
Если в обеих задачах пропустить операторы ге1еаве, это может привести к взаимной блокировке. Все это — примеры отказов механизма синхронизации взаимодействия. Проблемы надежности, связанные с использованием семафоров при синхронизации взаимодействия, возникают также при их использовании для синхронизации конкуренции. Если пропустить оператор назг (ассевв) в какой-либо из задач, зто может привести к несанкционированному доступу к буферу. Пропуск оператора ге1еаве (ассевв ) в какой-либо из задач приводит к взаимной блокировке. Все зто — примеры отказов механизма синхронизации конкуренции. Заметив опасность использования семафоров, Пер Бринч Хансен (Рег Вг)псЬ Напзеп) написал: "Семафор — это элегантный инструмент синхронизации для идеального программиста, никогда не допускающего ошибок" (Вппс!) Напзеп, 1973). К сожалению, такие программисты — большая редкость.
12.4. Мониторы Один из способов решения задач, связанных с использованием семафоров в параллельной среде, — инкапсулировать совместно используемые структуры данных вместе с операциями и скрыть их представление. т.е. слелать совместно используемые структуры данных абстрактными типами ланных. Это решение может обеспечить синхронизацию конкуренции без применения семафоров путем перекладывания ответственности за синхронизацию на систему полдержки выполнения программ.
12.4.з. Введение Когда были сформулированы понятия абстракции данных, люди, вовлеченные в их разработку, попытались применить те же концепции к совместно используемым данным в параллельных средах программирования, для того чтобы создать мониторы. Следуя идеям Пера Бринча Хансена (Вг!пс)з Напзеп, 1977, рабе хт!), Эдсгер Дийкстра предложил в 1971 году собрать все операции синхронизации над совместно используемыми данными в отдельный программный модуль. Бринч Хансен формализовал зто понятие применительно к операционным системам (Вг(пс)з Напзеп, 1973). В следующем году Хоар (Ноаге) назвал зти структуры мониторами (Ноаге, 1974). Первым языком программирования, содержащим мониторы, был Сопсцггеп! Рааса! (Вг!пей Напзеп, 1975).
Языки Моди)а (9(()п)з, 1977), СЬРЛс (Но!( ег а1., 1978) и Меза (Мпсйей ег а1., 1979) также поддерживают мониторы. Дальнейшее обсуждение мониторов основано на их реализации в языке Сопсцпеп( Рааса). Язык Сопсштепг Рааса) — это язык Рааса!, разработанный Виртом и дополненный тремя важными видами конструкций: классами нз языка Б!М()) А 67, процессами (так в языке Сопсцпепг Рааса! называются задачи) и мониторами.
Здесь мы обсудим свойства, связанные с поддержкой параллельного программирования: процессы и мониторы. Процесс в языке Сопсцггеп! Рааса) имеет синтаксическую форму, похожую на процедуру. но его семантика совершенно иная. Все процессы являются типами, поэтому они определяются операторами Куре следующего вида: Ку)ре имя процесса = ргосевв (формальные параметры) -- локальные объявления 316 Глава 12. Параллельность -- тело процесса а Поскольку процессы являются типами, их определения — это просто шаблоны фактических процессов. Поскольку для создания процессов используются объявления переменных, процессы можно созлавать как статически, так и динамически. Объявление переменной в качестве процесса создает код процесса, но не делает ничего, кроме этого. Чтобы разместить локальные данные процесса и начать его выполнение, следует использовать оператор диде с фактическими'параметрамн, как показано ниже: дпдк имя переменной процесса (фактические параметры) После выполнения оператора Дпйе процесс остается в состоянии текущей задачи на протяжении всей программы.
если он не будет заблокирован. Общий вид мониторов в языке Сопсцггепг Разса1 таков: куре имя монитора = жопъеок (формальные параметры) -- объявления совместно используемых переменных-- -- определения локальных процедур-- код инициализации-- а Экспортированные процедуры монитора синтаксически отличаются от локальных процедур только тем, что они содержат зарезервированное слово опеку в своих операторах ркооейике. Оператор з.пде с фактическими параметрами используется для создания экземпляров мониторов. Это вызывает размещение переменных процесса в динамической памяти и выполнение кода инициализации.