- •12. Лекция: Методы синхронизации процессов
- •Алгоритм решения проблемы критической секции
- •Алгоритм булочной (bakery algorithm)
- •Синхронизация на основе аппаратной поддержки атомарных операций
- •Синхронизация на основе общих семафоров
- •Реализация семафоров
- •Краткие итоги
- •Набор для практики Вопросы
- •Упражнения
- •Темы для курсовых работ, рефератов, эссе
Синхронизация на основе аппаратной поддержки атомарных операций
Рассмотренные алгоритмы синхронизации, не использующие каких-либо специальных синхронизирующих примитивов, достаточно сложны для понимания, разработки и сопровождения. Более простым (с точки зрения разработчика программ) решением для синхронизации была бы аппаратная и системная поддержка каких-либо простых атомарных операций, на основе которой реализовать синхронизацию процессов было бы проще.
Рассмотрим одну из этих операций, традиционно используемых для синхронизации, - операцию TestAndSet, которая атомарно выполняет считывание и запоминание значения переменной, затем изменяет его на заданное значение, но в результате выдает первоначальное значение переменной.
Предположим, что в системе имеется аппаратная поддержка следующей атомарной операции:
boolean TestAndSet (boolean & target) {
boolean rv = target;
target = true;
return rv;
}
С помощью данной операции реализовать синхронизацию процессов по критическим секциям очень просто. Введем в качестве блокировщика общую булевскую переменную:
boolean lock = false;
Код i-го процесса будет иметь вид:
do {
while (TestAndSet (lock));
критическая секция
lock = false;
остальная часть кода
} while (1)
Значение переменной lock, равноеtrue, означает, что вход в критическую секцию заблокирован. Каждый процесс ждет, пока он не разблокируется, затем, в свою очередь, выполняет блокировку и входит в критическую секцию. При ее завершении процесс разблокирует критическую секцию присваиваниемlock значенияfalse.
Другое распространенное аппаратное решение для синхронизации – атомарная операция Swap, выполняющая перестановку значений двух переменных:
void Swap (Boolean * a, Boolean * b) {
Boolean temp = * a;
a = * b;
* b = temp;
}
Взаимное исключение по критическим секциям с помощью атомарной операции Swap реализуется следующим образом (приведен кодi-го процесса) :
/* общие данные */
boolean lock = false;
Boolean key = false;
/* код процесса i */
do {
key = true;
while (key) {
Swap (&lock, &key);
}
критическая секция
lock = false;
остальная часть кода
} while (1)
При данной реализации, условием ожидания процесса перед входом в критическую секцию является условия (key == true), которое фактически означает то же, что и в предыдущей реализации, - закрытое состояние блокировщика, т.е., то, что другой процесс находится в своей критической секции. Когда критическая секция освободится (освобождение осуществляется присваиваниемlock = false после завершения критической секции в исполнившем ее процессе), ее начнет исполнять текущий процесс.
Синхронизация на основе общих семафоров
Мы уже начали рассматривать семафоры Дейкстры как средство синхронизации в обзорной части курса. Здесь мы рассмотрим их более подробно в общем виде. Общий семафор (counting semaphore), по Э. Дейкстре, - этоцелая переменнаяS, над которой определены две атомарных семафорных операцииwait (S) иsignal (S) со следующей семантикой:
wait (S):
while (S <= 0) do no-op;
S--;
signal (S):
S++;
Фактически, если начальное значение общего семафора равно n (> 0), то это число задает количество процессов, которые могут беспрепятственно выполнить над семафором операциюwait.
Синхронизация по критическим секциям с помощью общего семафора осуществляется следующим образом:
/* общие данные */
semaphore mutex = 1;
do {
wait (mutex);
критическая секция
signal (mutex);
остальная часть кода
} while (1)