Опубликован: 23.10.2005 | Уровень: специалист | Доступ: платный
Лекция 12:

Параллельность, распределенность, клиент-сервер и Интернет

Резюме параллельного механизма

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

Синтаксис

Синтаксис расширяется за счет введения только одного нового ключевого слова separate.

Объявление сущности или функции, которое в обычном случае выглядит как:

x: TYPE

сейчас может также иметь вид:

x: separate TYPE

Кроме этого, объявление класса, которое обычно начиналось с class C, deferred class C или expanded class C, сейчас может также иметь вид separate class C. В этом случае C называется сепаратным классом. Из этого синтаксического соглашения вытекает, что у класса может быть не более одного определяющего ключевого слова, например, он не может быть одновременно отложенным и сепаратным. Как и в случае развернутости и отложенности, свойство сепаратности класса не наследуется: класс является или не является сепаратным в соответствии с его собственным объявлением независимо от статуса сепаратности его родителя.

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

Ограничения

Правило корректной сепаратности состоит из четырех частей и управляет правильностью сепаратных вызовов:

  1. если источник присоединения (в инструкции присваивания или при передаче аргументов) является сепаратным, то его целевая сущность также должна быть сепаратной;
  2. если фактический аргумент сепаратного вызова имеет тип ссылки, то соответствующий формальный аргумент должен быть объявлен как сепаратный;
  3. если источник присоединения является результатом сепаратного вызова функции, возвращающей тип ссылки, то цель должна быть объявлена как сепаратная;
  4. если фактический аргумент или результат сепаратного вызова имеет развернутый тип, то его базовый класс не может содержать непосредственно или опосредованно никакой несепаратный атрибут ссылочного типа.

Ранее не приведенное, простое правило корректности для типов утверждает: в описании separate TYPE базовый класс для TYPE не должен быть ни отложенным, ни развернутым.

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

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

Семантика

Каждый объект обрабатывается некоторым процессором - его обработчиком. Если цель t инструкции создания не является сепаратной, то новый объект будет обрабатываться тем же процессором, что и создавший его объект. Если t сепаратна, то для обработки нового объекта будет назначен новый процессор.

Будучи создан, объект в дальнейшем находится в одном из двух состояний: свободен или зарезервирован (занят). Он свободен, если в данный момент не выполняется никакой его компонент и никакой сепаратный клиент не выполняет подпрограмму, использующую в качестве фактического аргумента присоединенную к нему сепаратную ссылку.

Процессор может находиться в трех состояниях: "не занят", "занят" и "приостановлен". Он "занят", если выполняет программу, чьей целью является обрабатываемый им объект. Он переходит в состояние "приостановлен" при попытке выполнить неуспешный вызов (это определено ниже), чья цель - обрабатываемый этим процессором объект.

Семантика вызова меняется только, если один или более вовлеченный в него элемент - цель или фактический аргумент - является сепаратным. Мы будем предполагать, что вызов имеет общий вид t.f (..., s, ...), в котором f - это подпрограмма. (Если f является атрибутом, то для простоты будем считать, что имеется вызов неявной функции, возвращающей значение этого атрибута.)

Пусть вызов выполняется как часть выполнения подпрограммы некоторого объекта C_OBJ, обработчик которого на этом шаге может быть только в состоянии "занят". Основным является следующее понятие:

Определение: выполнимый вызов (satisfiable call)

При отсутствии компонентов CONCURRENCY (описываемых далее) вызов подпрограммы f, выполняемый от имени объекта C_OBJ, является выполнимым тогда и только тогда, когда каждый сепаратный фактический аргумент, присоединенный к некоторому сепаратному объекту A_OBJ, чей соответствующий формальный аргумент используется подпрограммой как цель хотя бы одного вызова, удовлетворяет двум следующим условиям:

  • S1 A_OBJ свободен или зарезервирован (обработчиком) C_OBJ.
  • S2 Каждое сепаратное предложение предусловия f истинно для A_OBJ и заданных фактических аргументов.

Если процессор исполняет выполнимый вызов, то этот вызов называется успешным и осуществляется немедленно; C_OBJ остается зарезервированным, а его процессор остается в состоянии "занят", каждый объект A_OBJ становится зарезервированным, цель остается зарезервированной, обработчик цели становится занятым и начинает выполнение подпрограммы вызова. Когда этот вызов завершается, обработчик цели возвращается в свое предыдущее состояние ("не занят" или "приостановлен"), и каждый из объектов A_OBJ также возвращается в свое предыдущее состояние (свободен или зарезервирован (обработчиком) C_OBJ ).

Если вызов не является выполнимым, то он называется неуспешным; (обработчик) C_OBJ переходит в состояние "приостановлен". Эта попытка вызова никак не влияет на его цель и фактические аргументы. Если в некоторый момент один или больше ранее неуспешных вызовов становятся выполнимыми, то процессор выбирает один из них для выполнения (делает успешным). По умолчанию из нескольких выполнимых вызовов выбирается тот, который ожидал дольше других.

Последнее изменение в семантике связано с ожиданием по необходимости: если клиент запускает на некотором сепаратном объекте один или более вызовов и выполняет на этом объекте вызов запроса, то этот вызов может выполняться лишь после завершения всех ранее начавшихся, а все остальные операции клиента будут ждать завершения этого вызова запроса. (Мы уже отмечали, что оптимизирующая реализация может применять это правило только к запросам, возвращающим развернутый результат.) Во время ожидания завершения этих вызовов клиент остается в состоянии "зарезервирован".

Библиотечные механизмы

Компоненты класса CONCURRENCY позволяют в некоторых случаях считать, что условие выполнимости вызова S1 имеет место, даже если A_OBJ зарезервирован другим объектом ("обладателем") в случае, когда C_OBJ ("претендент") вызвал demand или insist ; если в результате этого вызов становится выполнимым, то обладатель получает некоторое исключение. Это может произойти только, если обладатель находится в состоянии "готов уступить", в которое можно попасть, вызвав yield.

Для возврата в предопределенное состояние "неуступчивости" обладатель может выполнить retain ; булевский запрос yielding позволяет узнать текущее состояние. Состояние претендента задается числовым запросом Challenging, который может иметь значения Normal, Demanding или Insisting.

Для возврата в предопределенное состояние Normal претендент может выполнить wait_turn. Различие между demand и insist заключается в том, что, если обладатель не находится в состоянии yielding, то при demand претендент получит исключение, а при insist он далее просто ожидает, как и при wait_turn.

Когда эти механизмы возбуждают исключение в обладателе или в претенденте, то булевский запрос is_concurrency_exception из класса EXCEPTIONS имеет значение "истина" ( true ).

Алексей Щербанов
Алексей Щербанов
Россия, г. Оренбург
Ксения Маковецкая
Ксения Маковецкая
Россия, Москва, МГЮА им. О.Е. Кутафина, 2014