
10.3. Проектирование разъемов для межзадачных коммуникаций
Классы-разъемы инкапсулируют детали межзадачных коммуникаций, например сильно и слабо связанный обмен сообщениями. Сервисы для межзадачных коммуникаций и синхронизации может предоставлять многозадачное ядро. Подобные механизмы есть также в некоторых языках параллельного программирования, в частности Ada или Java. Но ни один из этих языков не поддерживает слабо связанный обмен сообщениями. Чтобы реализовать указанную возможность, необходимо спроектировать скрывающий информацию класс MessageQueue, инкапсулирующий очередь сообщений и предоставляющий операции для доступа к ней. Классы такого вида называются разъемами.
Ниже будут описаны три класса-разъема для реализации слабо связанного обмена сообщениями, сильно связанного обмена без ответа и сильно связанного обмена с ответом. Каждый разъем представляет собой монитор, одновременно обеспечивающий синхронизацию и скрывающий информацию о том, как это делается. В разъемах применяется синхронизация по условию. Такие мониторы могут использоваться как в однопроцессорной так и в многопроцессорной системе с разделяемой памятью.
10.3.1. Проектирование разъема, реализующего очередь сообщений. Разъем, реализующий очередь сообщений, используется для инкапсуляции механизма слабо связанного обмена сообщениями. Это монитор, инкапсулирующий очередь, которая обычно существует в виде связанного списка. Разъем предоставляет синхронизированные операции send для отправки сообщения (вызывается задачей-производителем) и receive для получения сообщения (вызывается задачей-получателем) – рис.10.4. Производитель приостанавливается, если очередь заполнена (messageCount = maxCount) и возобновляет работу, когда освобождается место для размещения нового сообщения. Поместив сообщение в очередь, производитель продолжает работать и в состоянии посылать новые сообщения. Потребитель приостанавливается, когда очередь пуста (messageCount = 0) и активизируется, как только в очередь поступит сообщение. Потребитель не приостанавливается, если в очереди есть сообщения. Предполагается, что может быть несколько производителей и один потребитель.
Рис. 10.4. Пример разъема для реализации очереди сообщений
monitor MessageQueue
-- Инкапсулирует очередь сообщений,
-- рассчитанную максимум на maxCount
-- сообщений. Операции монитора выполняются
-- взаимно исключающим образом.
private maxCount : Integer;
private messageCount : Integer = 0;
public send (in message)
while messageCount = maxCount do wait;
Поместить сообщение в очередь;
Увеличить messageCount;
if messageCount = 1 then signal;
end send;
public receive (out message)
while messageCount = 0 do wait;
Извлечь сообщение из очереди;
Уменьшить messageCount;
if messageCount = maxCount - 1 then signal;
end receive;
end MessageQueue;
10.3.2. Проектирование разъема, реализующего буфер сообщений. Разъем, реализующий буфер сообщений, используется для инкапсуляции механизма сильно связанного обмена сообщениями без ответа. Это монитор, инкапсулирующий буфер на одно сообщение. Разъем предоставляет синхронизированные операции для отправки и получения сообщений (рис. 10.5). Производитель вызывает операцию send, а потребитель – операцию receive. Производитель приостанавливается, если буфер заполнен. Поместив сообщение в буфер, производитель ждет, пока потребитель примет его. Потребитель приостанавливается, когда буфер пуст. Предполагается, что может быть несколько производителей и один потребитель.
monitor MessageBuffer
-- Инкапсулирует буфер сообщений, в котором
-- может находиться не более одного
-- сообщения. Операции монитора выполняются
-- взаимно исключающим образом.
private messageBufferFull : Boolean = false;
private messageCount : Integer = 0;
public send (in message)
Поместить сообщение в буфер;
messageBufferFull = true;
signal;
while messageBufferFull = true do wait;
end send;
public receive (out message)
while messageBufferFull = false do wait;
Извлечь сообщение из буфера;
messageBufferFull = false;
signal;
end receive;
end MessageBuffer;
Рис. 10.5. Пример разъема для реализации буфера сообщений
10.3.3. Проектирование разъема, реализующего буфер сообщений с ответом. Разъем, реализующий буфер сообщений с ответом, используется для инкапсуляции механизма сильно связанного обмена сообщениями с ответом. Это монитор, инкапсулирующий буфер на одно сообщение и буфер на один ответ. Разъем предоставляет синхронизированные операции для отправки и получения сообщений, а также для отправки ответа (рис.10.6). Производитель вызывает операцию send для пересылки сообщения, а потребитель – операцию receive для приема сообщения и операцию reply для отправки ответа. Поместив сообщение в буфер, производитель ждет ответа от потребителя. Потребитель приостанавливается, когда буфер пуст. Предполагается, что может быть несколько производителей и один потребитель.
monitor MessageBuffer&Response
-- Инкапсулирует буфер сообщений, в котором
-- может находиться не более одного
-- сообщения, и буфер, в котором может
-- находиться не более одного ответа.
-- Операции монитора выполняются взаимно
-- исключающим образом.
private messageBufferFull : Boolean = false;
private responseBufferFull : Boolean = false;
private messageCount : Integer = 0;
public send (in message)
Поместить сообщение в буфер;
messageBufferFull = true;
signal ;
while responseBufferFull = false do wait;
Извлечь ответ из буфера;
responseBufferFull = false;
end send;
public receive (out message)
while messageBufferFull = false do wait;
Извлечь сообщение из буфера;
messageBufferFull = false;
end receive;
public reply (in response)
Поместить ответ в буфер;
responseBufferFull = true;
signal ;
end reply;
end MessageBuffer&Response
Рис.10.6. Пример разъема для реализации буфера сообщений с ответом
10.3.4. Проектирование кооперативных задач с использованием разъемов. Теперь рассмотрим проектирование группы кооперативных задач, общающихся между собой с помощью объектов-разъемов. Для иллюстрации воспользуемся примером из подсистемы Банкомат. Разъемы для этой подсистемы изображены на рис.10.7; есть два разъема – очереди сообщений и один разъем – буфер сообщений.
Объект Очередь Сообщений Управления Банкоматом инкапсулирует очередь входных сообщений задачи Контроллер Банкомата, для которой есть несколько производителей. Объект Очередь Сообщений Приглашений инкапсулирует очередь сообщений, посылаемых задачей Контроллер Банкомата задаче Интерфейс Клиента. И в том, и в другом случае производитель вызывает операцию send объекта-разъема, а потребитель – операцию receive того же объекта. Имеется также разъем буфер Сообщений Устройства Считывания, который инкапсулирует синхронный обмен без ответа между задачами Контроллер Банкомата и Интерфейс Устройства Считывания Карточек.
Наконец, есть объект заместитель Банковского Сервера. Он скрывает детали коммуникации с удаленным Банковским Сервером, применяя синхронный обмен сообщениями без ответа. Например, в языке Java этот заместитель воспользовался бы механизмом вызова удаленных методов (RMI), описанным ранее.
Рис.10.7. Пример кооперативных задач, использующих разъемы