logo search
Языки программирования

12.3. Проблема взаимных исключений

Проблема взаимных исключений (mutual exclusion problem) для параллельных программ является обобщением приведенного выше примера. Предполага­ется, что в каждой задаче (из параллельно выполняемых) вычисление делится на критическую (critical) и некритическую (non-critical) секцию (sec­tion), которые неоднократно выполняются:

task body T_i is

begin

loop

Prologue;

Critical_Section;

Epilogue;

Non_Critical_Section;

end loop;

end T_i:

Для решения проблемы взаимных исключений мы должны найти такие по­следовательности кода, называемые прологом (prologue) и эпилогом (epilogue), чтобы программа удовлетворяла следующим требованиям, которые должны выполняться для всех чередований последовательностей команд из набора задач:

Взаимное исключение. В любой момент времени только одна задача выпол­няет свою критическую секцию.

Отсутствие взаимоблокировки (no deadlock). Всегда есть, по крайней мере, одна задача, которая в состоянии продолжить выполнение.

Жизнеспособность. Если задаче необходимо выполнить критическую секцию, в конце концов она это сделает.

Справедливость. Доступ к критическому участку предоставляется «по справедливости».

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

Э. Дейкстра (E.W. Dijkstra) определил абстракцию синхронизации высо­кого уровня, называемую семафором, которая тривиально решает эту пробле­му. Семафор S является переменной, которая имеет целочисленное значе­ние; для семафоров определены две атомарные команды:

Wait(S): when S > 0 do S := S -1;

Signal(S): S:=S+1;

Процесс, выполняющий команду Wait(S), блокируется на время, пока значе­ние S неположительно. Обратите внимание, что, поскольку команда являет­ся атомарной, то, как только процесс удостоверится, что S положительно, он сразу уменьшит S (до того, как любой другой процесс выполнит команду!). Точно так же Signal(S) выполняется атомарно без возможности прерывания другим процессом между загрузкой и сохранением S. Проблема взаимных исключений решается следующим образом:

Ada

procedure Main is

S: Semaphore := 1 ;

task T_i; -- Одна из многих

task body T_i is

begin

loop

Wait(S);

Critical_Section;

Signal(S);

Non_Critical_Section;

end loop;

end T_i;

begin

null;

end Main;

Мы предлагаем читателю самостоятельно убедиться в том, что это реше­ние является правильным.

Конечно, самое простое — это переложить бремя решения проблемы на разработчика системы поддержки этапа выполнения.