- •Лекція №7 Паралельні обчислення: взаємовиключення і багатозадачність
- •7.1. Принципы параллельных вычислений
- •7.1.2. Взаимодействие процессов
- •7.1.3. Требования к взаимным исключениям
- •7.2. Взаимоисключения: программный подход
- •7.2.1. Алгоритм Деккера
- •Листинг 7.1. Алгоритм Деккера для двух процессов
- •7.2.2. Алгоритм Петерсона
- •Листинг 7.2. Алгоритм Петерсона для двух процессов
- •7.3. Взаимоисключения: аппаратная поддержка
- •7.3.2. Инструкция обмена
- •7.3.3. Свойства подхода, использующего машинные инструкции
Листинг 7.1. Алгоритм Деккера для двух процессов
Boolean flag [2];
int turn;
void P Ø ()
{
while (true)
{
flag [Ø] = true;
while (flag [1])
if (turn ==1)
{
flag [Ø] = false;
while (turn ==1) /* WAIT*/;
flag [Ø] = true;
}
/* критический раздел */
turn = 1;
flag [Ø] = false;
/*остальной код */
}
}
void P1 ()
{
while (true);
{
flag [1] = true;
while (flag [Ø])
if (turn ==Ø)
{
flag [1] = false;
while (turn == Ø); /* WAIT */
Flag[1] = true;
}
/* критический раздел */
turn = 0;
flag [1] = false;
/* остальной код */
}
}
void main ()
{
flag [Ø] = false;
flag [1] = false;
turn = 1;
parbegin (P Ø,P1);
}
7.2.2. Алгоритм Петерсона
Алгоритм Деккера решает задачу взаимных исключений, но достаточно сложно, и вдобавок тяжело доказать его корректность. В 1981 году Петерсон (Peterson) предложил более изящное и простое решение проблемы взаимных исключений. Как и раньше, глобальная сменная flag указывает на положение каждого процесса по отношению к взаимоисключениям, а глобальная переменная turn решает конфликты одновременности. При исполнении пролога критической секции процесс Pi заявляет о своей готовности выполнить критический участок и одновременно предлагает другому процессу приступить к его выполнению. Если оба процесса подошли к прологу практически одновременно, то они оба объявят о своей готовности и предложат выполняться друг другу. При этом одно из предложений всегда следует после другого. Тем самым работу в критическом участке продолжит процесс, которому было сделано последнее предложение.
Листинг 7.2. Алгоритм Петерсона для двух процессов
boolean flag[2];
int turn;
void PØ()
{
while (true)
{
flag[Ø] = true;
turn = 1;
while (flag[1] && turn == 1); /* ждать */
/* критический раздел */
flag[Ø] = false;
/* остальной код */;
}
}
void p1()
{
while (true)
{
flag[1] = true;
turn = 0;
while (flag[Ø]) && turn == 0);/* ждать */
/* критический раздел */
flag[1] = false;
/* остальной код */
}
}
void main()
{
flag[Ø] = false;
flag[1] = false;
turn = 1;
parbegin(P Ø,P1);
}
Выполнение условий взаимоисключения легко доказать. Рассмотрим процесс Р0. После того как flag[0] установленный в true, Р1 войти в критический раздел не может. Если же Р1 уже находится в критическом разделе, то flag[1] = true и для Р0 вход в критический раздел заблокирован. В данном случае взаимное блокирование предотвращено. Предположим, что Р0 заблокирован в своем цикле whіle. Это означает, что flag[1] = true, а turn = 1. Р0 может войти в критический раздел, когда либо flag[1] становится равным false, либо turn становится равным 0. Рассмотрим три исчерпывающих случая.
1. Р1 не намерен входить в критический раздел. Такой случай невозможен, поскольку при этом выполнялось бы условие flag[1] = false.
2. Р1 ожидает вход в критический раздел. Такой случай также невозможным, поскольку, если turn = 1, то Р1 способен войти в критический раздел.
3. Р1 циклически использует критический раздел, монополизируя доступ к нему. Этого не может произойти, поскольку Р1 вынужден перед каждой попыткой вхождения в критический раздел дать такую возможность процессу Р0, устанавливая значение turn равным 0.
Следовательно, у нас есть простое решение проблемы взаимных исключений для двух процессов. Алгоритм Петерсона легко обобщается на случай n процессов.