- •Глава 6 Проектирование параллельных взаимодействующих вычислительных процессов
- •Независимые и взаимодействующие вычислительные процессы
- •Средства синхронизации и связи при проектировании взаимодействующих вычислительных процессов
- •1 На наш взгляд, эта работа Дейкстры очень полезна с методической точки зрения. Она позволяет понять наиболее тонкие моменты в этой проблематике.
- •Возможные проблемы при организации взаимного исключения посредством использования только блокировки памяти
- •Var перекл : integer:
- •Van перекл1.Перекл2.: boolean: begin nepewil:-false;
- •Алгоритм Деккера
- •Синхронизация процессов посредством операции «проверка и установка»
- •1 Cf (carry flag) — флаг переноса. Располагается в слове состояния программы. Для процессоров i80x86 этот регистр — управляющий регистр cro.
- •Семафорные примитивы Дейкстры
- •Мьютексы
- •Пример простейшей синхронизации взаимодействующих процессов
- •Решение задачи «читатели — писатели»
- •Мониторы Хоара
- •Почтовые ящики
- •1 Название «почтовый ящик» происходит от обычного приспособления для отправки почты.
- •Очереди сообщений
- •Примеры создания параллельных взаимодействующих вычислительных процессов
- •Пример создания многозадачного приложения с помощью системы программирования Borland Delphi
- •Пример создания комплекса параллельных взаимодействующих программ, выступающих как самостоятельные вычислительные процессы
Мьютексы
Одним из вариантов семафорных механизмов для организации взаимного исключения являются так называемые мьютексы (mutex). Термин mutex произошел от английского словосочетания mutual exclusion semaphore, что дословно и переводится как семафор взаимного исключения. Мьютексы реализованы во многих ОС, их основное назначение — организация взаимного исключения для задач (потоков) из одного и того же или из разных процессов. Мьютексы — это простейшие двоичные семафоры, которые могут находиться в одном из двух состояний — отмеченном или неотмеченном (открыт и закрыт соответственно). Когда какая-либо задача, принадлежащая любому процессу, становится владельцем объекта mutex, последний переводится в неотмеченное состояние. Если задача освобождает мьютекс, его состояние становится отмеченным.
Организация последовательного (а не параллельного) доступа к ресурсам с использованием мьютексов становится несложной, поскольку в каждый конкретный момент только одна задача может владеть этим объектом. Для того чтобы объект mutex стал доступен задачам (потокам), принадлежащим разным процессам, при создании ему необходимо присвоить имя. Потом это имя нужно передать «по наследству» задачам, которые должны его использовать для взаимодействия. Для этого вводятся специальные системные вызовы (CreateMutex), в которых указываются начальное значение мьютекса, его имя и, возможно, атрибуты защиты. Если начальное значение мьютекса равно true, то считается, что задача, создающая этот объект, будет им сразу владеть. Можно указать в качестве начального значение false — в этом случае мьютекс не принадлежит ни одной из задач и только специальным обращением к нему можно изменить его состояние.
Для работы с мьютексом имеется несколько функций. Помимо уже упомянутой функции создания такого объекта (CreateMutex), есть функции открытия (Ореп-Mutex), ожидания событий (WaitForSingleObject и WaitForMultipleObjects) и, наконец, освобождение этого объекта (ReleaseMutex).
Конкретные обращения к этим функциям и перечни передаваемых и получаемых параметров нужно смотреть в документации на соответствующую ОС.
Использование семафоров при проектировании взаимодействующих вычислительных процессов
Семафорные примитивы чрезвычайно широко используются при проектировании разнообразных вычислительных процессов. При этом некоторые задачи являются настолько «типичными», что их детальное рассмотрение уже стало классическим в соответствующих учебных пособиях. Не будем делать исключений и мы.
Задача «поставщик — потребитель»
Решение задачи «поставщик - потребитель» является характерным примером использования семафорных операций. Содержательная постановка этой задачи уже была нами описана в первом разделе данной главы. Разделяемыми переменными здесь являются счетчики свободных и занятых буферов, которые должны быть защищены со стороны обоих процессов, то есть действия по посылке и получению сообщений должны быть синхронизированы. Использование семафоров для решения данной задачи приведено в листинге 6.11.
Листинг 6.11. Решение задачи «поставщик — потребитель»
var 5_свободно,5_заполнено.5_вэаимоискл : semaphore; begin
InitSem(S_CBo6oflHO.N); 1пП5ет(5_залолнено.О): 1пН5ет($_взаимоискл,1); parbegin
ПОСТАВЩИК: while true do begin
{ приготовить сообщение } Р(5_свободно); Р(5_взаимоискл);
( послать сообщение } \К5_заполнено): У(5_взаимоискл); end and
ПОТРЕБИТЕЛЬ: while true do begin
Р(5_заполнено): Р(5_взаииоискл):
{ получить сообщение } V(S_ceo6oflHo): У(5_взаимоискл):
{ обработать сообщение } end pa rend end.
Здесь переменные 5_свободно, 5_заполнено являются числовыми семафорами, 5_взаиноискл - двоичный семафор. 5_свободно имеет начальное значение, равное N, где N - количество буферов, с помощью которых процессы связываются. Предполагается, что в начальный момент количество свободных буферов равно N; соответственно, количество занятых равно нулю. Двоичный семафор S вэаи-моискл гарантирует, что в каждый момент только один процесс сможет работать с критическим ресурсом, выполняя свой критический интервал. Семафоры 5_сво-бодно и 5_заполнено используются как счетчики свободных и заполненных буферов, i
Действительно, перед посылкой сообщения «поставщик» уменьшает значение 5_свободно на единицу в результате выполнения Р(5_свободно), а после посылки сообщения увеличивает значение 5_заполненО на единицу в результате выполнения У(5_эаполнено). Аналогично, перед получением сообщения «потребитель» уменьшает значение 5_заполнено в результате выполнения Р(5_заполнено), а после получения сообщения увеличивает значение 5_свободно в результате выполнения У(5_свободно). Семафоры 5_заполнено, 5_свободно могут также использоваться для блокировки соответствующих процессов. Если пул буферов оказался пустым и к нему первым обратится процесс «потребитель», он заблокируется на семафоре 5_заполнено в результате выполнения Р(5_заполнено). Если пул буферов заполнен и к нему в этот момент обратится процесс «поставщик», то он будет заблокирован на семафоре 5_свободно в результате выполнения Р(5_свободно). В решении задачи о «поставщике» и «потребителе» общие семафоры применены для учета свободных и заполненных буферов. Их можно также применить и для распределения иных ресурсов.
