Спонсор: Microsoft
Опубликован: 13.11.2010 | Уровень: для всех | Доступ: свободно | ВУЗ: Санкт-Петербургский государственный университет
Лекция 12:

Методы синхронизации процессов

Решение с помощью семафоров задачи "читатели – писатели"

Суть задачи читатели-писатели в следующем: имеется общий ресурс и две группы процессов: читатели (которые могут только читать ресурс, но не изменяют его) и писатели (которые изменяют ресурс). В каждый момент работать с ресурсом может сразу несколько читателей (когда ресурс не изменяется писателями), но не более одного писателя. Необходимо синхронизировать их действия над ресурсом и обеспечить взаимное исключение соответствующих критических секций.

Будем использовать два семафора и целую переменную:

semaphore mutex = 1;

semaphore wrt = 1;

int readcount = 0;

Семафор mutex используется читателями для взаимного исключения операций над переменной readcount (счетчиком читателей). Семафор wrt используется для взаимного исключения писателей.

Реализация процесса-писателя особых комментариев не требует:

wait (wrt);

. . .

изменение ресурса

. . . 

signal (wrt);

Реализация процесса-читателя несколько более сложна:

wait (mutex);

readcount++;

if (readcount == 1) {

	wait (wrt);

}

signal (mutex);

. . .

чтение ресурса 

. . .

wait (mutex);

readcount--;

if (readcount == 0) {

	signal (wrt);

}

Процесс-читатель, во-первых, должен увеличить значение readcount, причем обеспечить взаимное исключение для действий над readcount с помощью семафора mutex.Далее, если процесс является первым читателем, он должен закрыть семафор wrt, чтобы исключить одновременное с чтением изменение ресурса писателями. По окончании чтения ресурса, читатель в аналогичном стиле вновь уменьшает readcount. Если при этом оно обнуляется (т.е. это последний на данный момент читатель), то читатель открывает семафор wrt, сигнализируя писателям, что они могут изменять ресурс.

Решение с помощью семафоров задачи "обедающие философы"

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

Задача "обедающие философы".

Рис. 12.1. Задача "обедающие философы".

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

При решении задачи будем использовать массив семафоров chopstick, описывающий текущее состояние палочек: chopstick[i] закрыт, если палочка занята, открыт – если свободна:

semaphore chopstick [5] = { 1, 1, 1, 1, 1};

Алгоритм реализации действий философа i имеет вид:

do {

	wait (chopstick [i]); /* взять левую палочку */

	wait (chopstick [(i + 1) % 5]); /* взять правую палочку */

	. . . 

	обедать

	. . .

	signal (chopstick [i]); /* освободить левую палочку */

	signal (chopstick [(i+1) % 5]); /* освободить правую палочку */

	. . . 

	думать

	. . .

while (1);

Решение данной задачи с помощью семафоров оказывается особенно простым и красивым.

Критические области

Критические области (critical regions) – более высокоуровневая и надежная конструкция для синхронизации, чем семафоры. Общий ресурс описывается в виде особого описания переменной:

v: shared T

Доступ к переменной v возможен только с помощью специальной конструкции:

region v when B do S

где v – общий ресурс; B – булевское условие, S – оператор (содержащий действия над v ).

Семантика данной конструкции следующая. Пока B ложно, процесс, ее исполняющий, должен ждать. Когда B становится истинным, процесс получает доступ к ресурсу v и выполняет над ним операции S. Пока исполняется оператор S, больше ни один процесс не имеет доступа к переменной v.

Решение с помощью критических областей задачи "ограниченный буфер"

Опишем буфер как структуру:

struct buffer {

	int pool [n];

	int count, in, out

}

buf: shared buffer;

Алгоритм процесса-производителя:

region buf when (count < n) {

	pool [in] = nextp;

	in = (in+1) % n;

	count++;

}

Заметим, что проверка переполнения буфера выполняется при входе в конструкцию region, и потребитель ждет, пока в буфере освободится хотя бы один элемент.

Алгоритм процесса-потребителя:

region buf when (count > 0) {

	nextc = pool [out];

	out = (out+1) % n;

	count--;

}

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

Схема реализации критических областей с помощью семафоров

Будем использовать для реализации конструкции region x when B do S следующие семафоры и целые переменные:

semaphore mutex, first-delay, second-delay;

int first-count, second-count;

Семафор mutex используется для взаимного исключения доступа к критической секции S. Семафор first-delay используется для ожидания процессов, которые не могут войти в критическую секцию S, так как условие B ложно. Число таких процесов хранится в переменной first-count. Семафор second-delay используется для ожидания тех процессов, которые вычислили условие B один раз и ожидают, когда им будет позволено повторно вычислить B ( second-count счетчик таких процессов). Реализация предоставляется студентам в качестве упражнения.

Гульжан Мурсакимова
Гульжан Мурсакимова
Василий Четвертаков
Василий Четвертаков