- •Лекция 7. Синхронизация параллельных процессов. Параллельная обработка
- •Проблемы критических участков. Взаимоисключения
- •Синхронизация в qnx
- •Синхронизация параллельных процессов на низком уровне Блокировка памяти
- •Алгоритм Деккера (Dekker’s Algorithm)
- •Аппаратная реализация взаимоисключения: команда “проверка и установка” (testandset)
- •Аппаратная реализация синхронизации
- •Инструкция "обменять содержимое переменных"
- •Семафоры
- •Синхронизация блокирования/возобновления процессов при помощи семафоров
- •Реализация взаимодействия в паре “производитель-потребитель” при помощи семафоров
- •Мониторы
- •Команды Wait () и Signal ().
- •Монитор, реализующий двоичный семафор.
- •Решение задачи передачи данных одного процесса другому при помощи монитора (случай кольцевого буфера)
- •Решение задачи передачи данных одного процесса другому при помощи монитора (случай информационной базы)
- •Службы синхронизации
- •Блокировки взаимного исключения (мьютексы)
- •Наследование приоритетов
- •Условные переменные
- •Барьеры
- •Ждущие блокировки
- •Блокировки по чтению/записи
- •Семафоры
- •Синхронизация с помощью алгоритма планирования
- •Синхронизация с помощью механизма обмена сообщениями
- •Синхронизация с помощью атомарных операций
- •Реализация служб синхронизации
Инструкция "обменять содержимое переменных"
В наборе инструкций процессоров семейства Intel x86 существует инструкция xchg (exchange), которая имеет следующую реализацию:
void xchg(register int r, int x) {
int temp; temp = r; r = x; x = temp; }
и выполняется непрерывным образом, т. е. выполнение этой инструкции не прерывается. Используя эту инструкцию, можно реализовать взаимоисключающий доступ параллельных потоков к критической секции по следующему алгоритму:
int lock =0; // переменная, которая запирает вход в критическую
// секцию
void thread()
{
while(true)
{
register int key; // ключ к замку
key = 1;
while(key == 1) // ждать, пока замок закрыт
xchg(key, lock);
critical section(); // критическая секция lock =0; // открыть замок
non_critical_section(); // остальной код
}
}
По аналогии с алгоритмом, использующим инструкцию tas, можно показать, что приведенный алгоритм также удовлетворяет двум первым требованиям, выдвигаемым к решению задачи взаимного исключения.
Для того чтобы терминологию, относящуюся к аппаратной синхронизации потоков, сделать независимой от типов команд, которые используются в процессорах для этих целей, ввели такое понятие, как спин блокировка (spinlock) или активное ожидание. Спин блокировкой называется цикл while с непрерываемой командой процессора, который ждет разрешения на вход в критическую секцию.
Во-первых, аппаратные алгоритмы синхронизации могут использоваться любым количеством параллельных потоков. Во-вторых, как программные, так аппаратные алгоритмы синхронизации имеют один существенный недостаток: впустую тратится процессорное время в циклах while, ждущих разрешения на вход в критическую секцию. Поэтому все эти алгоритмы получили общее название алгоритмы, занимающиеся ожиданием (busy waiting algorithms), или алгоритмы активного ожидания.
Семафоры
Семафор или общий семафор (semaphore) - это целая переменная, значение которой можно опрашивать и менять только при помощи специальных неделимых (как команда testandset) операций P и V.
Двоичный семафор может принимать только значения 0 или 1.
Мьютекс (mutex, mutual exclusion semaphore) – семафор взаимного исключения. Основная задача – организация взаимного исключения для потоков из одного или разных процессов. Мьютекс является двоичным семафором.
P Proberen (гол) проверить (down)
V Verhogen (гол)увеличить (up)
Смысл примитива P(S) – проверка текущего значения семафора S, и если оно не меньше 0, то осуществляется переход к следующей за примитивом операции, в противном случае процесс снимается на некоторое время с выполнения и переводится в состояние пассивного ожидания.
Операция V(S) увеличивает значение семафора на единицу и переводит процесс в состояние готовности.
P(S): S:= S-1;
If S< 0 then {ожидание к S}
V(S): if S<0 then {один процесс в готовые}
S:=S+1;
Будем предполагать, что очередь процессов, ожидающих на семафоре, обслуживается в соответствии с дисциплиной FIFO.
Приведем пример алгоритма обеспечения взаимоисключения при помощи семафора. Инициализацию семафора будем производить с помощью операции, условно обозначенной Инициализация_S(S,1), которая устанавливает для семафора начальное значение 1 или Иницилизация_S(S,0),
соответственно, 0.
var S : семафор;
procedure процесс_Х;
begin
while (true) do
begin
{Предшествующая критическому участку
часть процесса_Х}
P(S);{если в семафоре 1, то S:=S-1, иначе ожидать}
{Критический участок процесса_Х}
V(S);{если процесс ожидает на S, то разрешить работу, иначе S:=S+1}
{Остальная часть процесса_Х}
end
end;
procedure процесс_Y;
begin
while (true) do
begin
{Предшествующая критическому участку
часть процесса_Y}
P(S); {вход взаимоисключения, процесс ожидает выполнения операции V(S) процессом_Х }
{Критический участок процесса_Y}
V(S); {выход взаимоисключения}
{Остальная часть процесса_Y}
end
end;
begin
Инициализация_S(S,1);
Parbegin
процесс_Х;
процесс_Y;
Parend
end.
