Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Брой распознанный текст.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
1.18 Mб
Скачать

1.3. Языки программирования для описания взаимодействующих систем

Для распределенных систем особое значение имеют действия, которые служат для обмена информацией (коммуникация и координация) между разделенными (параллельно) работающими компонентами системы. Даже синхронизирующие действия могут трактоваться как виды обмена ин­формацией (над управляющими состояниями). Языки программирования для описания процессов содержат, в частности, операторы для обмена сообщениями между параллельно выполняющимися программами.

1.3.1. Коммуникация через обмен сообщениями

Обмен сообщениями между двумя компонентами в параллельно рабо таюшей системе может в принципе достигаться с помощью двух допол нительных действий: отправления и получения. В одном процессе от­правление/получение могут быть задействованы многие партнеры. На пример, один отправитель может одновременно посылать сообщение многим получателям (адресатам) (англ. broadcasting). Однако часто огра ничиваются системами, в которых для каждого пересылаемого в системе сообщения существует единственный отправитель и самое большее один адресат.

Важным аспектом при обмене сообщениями является концепция ад­ресации получателя. При посылке сообщения во всяком случае должен быть специфицирован получатель или крут возможных получателей. Аналогично при получении должен быть специфицирован желаемый от­правитель или круг отправителей. При попытке получить сообщение (точнее, при опросе, имеется ли налицо сообщение) должен быть назван отправитель или крут возможных отправителей. Существуют самые раз­личные способы адресации сообщений. Например, для адресации могут использоваться имена (метки) параллельно выполняющихся программ.

Каналы для обмена сообщениями типа ш объявляются аналогично о&ьяатеншо программных переменных:

channel ш с

Среда передачи сообщений, которая служит для установления связи меж­ду отправителем и получателем, носит название канал. С помощью кана­лов могут адресоваться сообщения. Отправитель в действии отправки на­зывает не имя получателя, а только имя канала. Аналогично обстоит дело и для получателя. В процедурном ("императивном", т. е. ориентирован­ном на присваивания) языке действия пересылки по каналу с выражают­ся с помощью оператора

send Е on с

где Е - любое выражение типа ш ; с - имя канала.

В дополнение к этому действие приема сообщения выражается опе­ратором

receive х on с

где х - идентификатор программной переменной типа m ; с - имя канала.

Если к моменту выполнения действия приема еще отсутствует посланная информация, то процесс "ждет" до поступления сообщения. Если пере­сылка никогда не последует, то действие приема не завершается, что со­ответствует .локальному тупику.

Мы расширим синтаксис для формулирования императивной про­граммы, как он был описан в части I, до описания параллельной компо­зиции ||, описания каналов, операторов посылки и приема сообщений. Возникает расширенный процедурный язык, который может использо­ваться для формулирования параллельно выполняющихся программ, об­менивающихся сообщениями.

Пример (производитель/потребитель). Следующая схема программы со­стоит из двух параллельно выполняющихся последовательных программ, из которых вторая посылает сообщения первой.

Г channel m с;

var m х, var m z := xo, zo; fT while —b(x) do receive x on c;

consume (x)

od

II

while —b(z) do produce_next(z); send z on с

od il J

Здесь consume и poduce_next - заданные процедуры, a b - булевская функция. Из этой схема можно получить, например, параллельную про­грамму' дтя вычисления функции факториал - за счет того, что мы выбе­рем следующие конкретные процедуры:

consume(х) н if х * 0 then г := х «г fi ,

produce_next(z) = z:=z~ I, b(x) = (x = 0) .

Дополнительно пусть г - программная переменная, которой первона­чально присвоено значение 1, и пусть справедливо

х0 = zo = п+1.

Тогда после выполнения программы имеет место г = п!.

Символ IT стоит в качестве parbegin, т. е. обозначения начала раздела, ко­торый состоит из некоторого числа параьлельно выполняющихся опера­торов. Эти операторы разделяются символом ||. Раздел завершается сим­волом JJ, который стоит в качестве parend - этот символ помечает конец раздела, состоящего из параллельных операторов.

Различают два основных способа обмена сообщениями между отправите­лем и получателем;

(1) неявные буферы: готовый к посылке передающий процесс не додже!) ждать готовности к приему принимающего процесса. Сообщение по­сылается немедленно, и в случае необходимости оно помещается на промежуточное хранение, после чего посылающий процесс продол­жает свою работу. Соответствующие события отправителя и получа­теля находятся в причинной связи. Посылка является причиной для получения;

(2) рандеву ("рукопожатие"): посылающий процесс ждет, пока прини­мающий процесс будет готов к приему сообщения. Обмен инфор­мацией имеет место в событии, после чего оба процесса продолжают свою работу. Обмен сообщениями соответствует общему событию.

В случае (1) говорят об асинхронной, а в случае (2) - о синхронной передаче. Необходимо тщательно различать эти две концепции, так как взаимодей­ствующие программы могут проявлять различное поведение в зависимо­сти от того, какая концепция коммуникации ими поддерживается.

Р

I

S/E

/ \

С Р

\ /

S/E

/ \

С Р

\ /

Рис. 1.34. Процесс производитель/потребитель с синхронной пересылкой/получением

Для обсуждения различных концепций обмена сообщениями введем две синтаксические формы. В дальнейшем мы будем использовать операторы

send Е on с

и соответственно

receive х on с

для асинхронного обмена сообщениями с неявными буферами, и будем писать

Е ! с

для синхронного действия посылки и X ? с

для синхронного действия приема при обмене сообщениями через ранде­ву.

При этом соглашении наш пример производитель/потребитель в но­тации рандеву может быть записан следующим образом:Г channel ш с;

var m x, var m z := xo, z0; [Г while -'b(x) do x ? c;

consume (x)

od

while ~^b(z) do produce_next(z); z ! с

od

Дтя задачи производитель/потребитель шггерпретапия рандеву допускает по сути дела только единственный ход работы, показанный на рис. 1.34 (где С обозначает действие coasume(x), Р - действие produce_next(z), а S/E - общее действие обмена сообщением z ! с/х ? с).

Неявные буферы допускают значительно больший параллелизм, чем синхронный обмен сообщениями. Для случая производитель/потребитель мы получаем процесс, заданный на рис. 1.35.

Р Е

I J

S с

IV

Е

С

Рис. 1.35. Процесс производитель/потребитель с асинхронным обменом сообщениями

Единственное ограничение в параллельной работе обоих последователь­ных процессов здесь состоит в том, что сообщение не может быть приня­то до того, как оно будет послано. Действие посылки является причин­ным предположением для соответствующего действия по приему сооб­щения.

il J

Если мы от параллельной программы Р, в которой обмен сообще­ниями осуществляется по принципу неявного буфера, переходим к про­

грамме Р', осуществляющей обмен по принципу рандеву, то получается следующее отношение между этими программами:

  1. Каждый свободный от тупиков ход работы программы р' есть линеа­ризация хода работы р, причем действия посылки/приема сообщений могут толковаться как одно действие.

  2. Определенные ходы работы р и их линеаризации исключаются как ходы работы р'.

  3. Р', возможно, приведет к тупикам, которые исключены для Р.

Неявные буферы реже приводят к тупикам, однако требуется больше места в памяти для промежуточного хранения сообщений.

Пример (синхронная и асинхронная коммуникация). Рассмотрим сле­дующие две программы:

    1. [Г 1 ! с 1; х ? с2 II 2 ! с2; у ? с 1 JJ

    2. IT send 1 on cl; receive x on c2 || send 2 on c2; receive у on cl 11

Программа (1) приводит к тупику', поскольку оба процесса ждут отправи­теля, тогда как программа (2) свободна от тупиков. С

При концепции неявного буфера система параллельных программ может завершить работу даже в том случае, когда приняты не все из посланных сообщений. При концепции рандеву это не имеет места. Вопрос о том, какой способ обмена сообщениями является более подходящим, сущест­венно зависит от применений.

Через концепцию канала для действий посылки/приема сообщений с самого начала не устанавливается какого-либо однозначного отноше­ния между отправителем и получателем - и тот и другой обращаются к каналам. Какой получатель и какое сообщение в конце концов он по­лучит в случае многих одновременных операций посылки и многих гото­вых к приему получателей, программой не предопределяется. Поэтому снова речь идет о недетерминированности.

Пример (недетерминированность при посылке/приеме). В программе

Г var nat xl, х2 := 0, 0; channel nat с;

IT send 1 on с I send 2 on с [| receive xl on с || receive x2 on с JJ J

не устанавливается, будет ли после ее выполнения достигнуто состояние, для которого справедливо высказывание

(xl = 1 л х2 = 2)

или состояние, для которого справедливо высказывание

(х2=1лх1 = 2). □

Недетермшшрованность возникает типичным образом при обмене сооб­щениями, когда многие отправители или получатели параллельно хотят обмениваться по одному канату. Канал ведет к линеаризации обмена со­общениями и тем самым к, возможно, недетерминированному упорядо­чению.