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

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

Аннотация: В лекции рассмотрена синхронизация процессов: критические секции; алгоритмы решения проблемы взаимного исключения критических секций; двоичные и общие семафоры; решение проблем "ограниченный буфер", "читатели-писатели", "обедающие философы"; мониторы; синхронизация в Solaris и Windows 2000.
Ключевые слова: синхронизация процессов, параллельные алгоритмы, ПО, критическая секция, критическая область, совместный доступ, параллельный процесс, буфер, представление, counter, Си, переменная, доступ, значение, операции, атомарная операция, регистр, операторы, исполнение, interleaving, turn, очередь, алгоритм, массив, доказательство, автор, choose, алгоритм булочной, поддержка, атомарность, завершение процесса, swapping, исключение, семафор Дейкстры, средство синхронизации, signaling, список, процессор, Дополнение, переполнение буфера, mutex, блокировка, ресурс, группа процессов, WRT, параллельное программирование, массив семафоров, описание переменной, семантика, delay, счетчик, conditioning, EAT, монитор, процедуры монитора, условная переменная, параметр, многозадачность, синхронизация потоков, adaptability, защита доступа, многопроцессорность, определение, windows 2000, многопроцессорная система, механизм синхронизации, модуль, Задача синхронизации, мьютекс, бит, команда, прерывание, работ, race condition, время ожидания, Test-and-Set, перестановка, ссылка, block, разделяемый ресурс, region

Презентацию к данной лекции Вы можете скачать здесь.

Введение

В лекции рассматривается синхронизация процессов – одна из интереснейших и наиболее актуальных тем, в связи с широким распространением параллельных вычислительных систем и параллельных алгоритмов решения задач, требующих синхронизации параллельных процессов и потоков по общим ресурсам и событиям. В лекции рассмотрены следующие вопросы:

  • История синхронизации процессов
  • Проблема критической секции
  • Аппаратная поддержка синхронизации
  • Семафоры
  • Классические проблемы синхронизации
  • Критические области
  • Мониторы
  • Синхронизация в Solaris и в Windows.

История синхронизации

Методы синхронизации процессов рассматривались еще в 1960-х гг. в пионерской работе Э. Дейкстры [ 17 ] . Было отмечено, что совместный доступ параллельных процессов к общим данным может привести к нарушению их целостности. Поддержание целостности общих данных требует реализации и использования механизмов упорядочения работы взаимодействующих процессов (или потоков).

Анализ проблемы производитель – потребитель с точки зрения синхронизации по общему буферу

Вернемся к уже рассмотренной нами проблеме (парадигме) взаимодействия процессов производитель – потребитель (см. "Методы взаимодействия процессов" ). Имеется общий буфер ограниченной длины. Процесс-производитель добавляет в него сгенерированные элементы, процесс-потребитель использует и удаляет использованные элементы. Добавим в представление ограниченного буфера переменную counter,которую увеличивает процесс-производитель, добавляя очередной элемент к буферу, и уменьшает процесс-потребитель, используя и удаляя элемент из буфера.

Вспомним представление ограниченного буфера на языке Си (см. "Методы взаимодействия процессов" ) и расширим его переменной counter :

#define BUFFER_SIZE 1000 /* или другое конкретное значение */

typedef struct {

	. . .

} item;

item buffer[BUFFER_SIZE];

int in = 0;

int out = 0;

int counter = 0;

Теперь модифицируем реализации процесса-производителя и процесса-потребителя (см. "Методы взаимодействия процессов" ) , добавив соответствующие изменения переменной counter:

Процесс-производитель:

item nextProduced; /* следующий генерируемый элемент */

	while (1) { /* бесконечный цикл */

	while (counter == BUFFER_SIZE)

		; /* ждать, пока буфер переполнен */

	buffer[in] = nextProduced; /* генерация элемента */

	in = (in + 1) % BUFFER_SIZE;

	counter++;

}

Процесс-потребитель:

item nextConsumed; /* следующий используемый элемент */

while (1) { /* бесконечный цикл */

	while (counter == 0)

		; /* ждать, пока буфер пуст */

	nextConsumed = buffer[out]; /* использование элемента */

	out = (out + 1) % BUFFER_SIZE;

	counter--;

}

Возникает вопрос: насколько корректны модифицированные алгоритмы, использующие переменную counter ? Не вполне. Проблема в том, что counter фактически является общим ресурсом, к которому одновременно обращаются два параллельных процесса. Если при этом обращение произойдет одновременно, то переменная counter может в итоге оказаться в некорректном состоянии. Поэтому необходимо, чтобы каждый из процессов при увеличении или уменьшении ее значения имел бы к ней монопольный доступ, и другой процесс не мог бы в это время "испортить" значение переменной. Иными словами, операции counter++ и counter должны выполняться атомарно (см. лекцию 9). Напомним, что атомарная операция – это такая операция, которая должна быть выполнена полностью, без каких-либо прерываний; при этом операция, выполняемая одним из процессов, должна быть неделимой, с точки зрения другого процесса.

Операции count++ и count— могут быть реализованы на языке ассемблерного уровня следующим образом:

count++:

register1 = counter

register1 = register1 + 1

counter = register1

count--:

register1 = counter

register1 = register1 - 1

counter = register1

где register1 регистр аппаратуры.

Проблема в том, что если и производитель, и потребитель пытаются изменить переменную counter одновременно, то указанные ассемблерные операторы тоже должны быть выполнены совместно (interleaved).

Конкретная реализация такого совместного выполнения зависит от того, каким образом происходит планирование для процессов – производителя и потребителя, а также от применения (или неприменения) в каждом из случаев аппаратных оптимизаций, например, увеличения (уменьшения) значения регистра одной командой за один такт ( increment / decrement).

Рассмотрим эффект interleaving на конкретном примере. Предположим, что начальное значение переменной counter в некоторый момент равно 5. Исполнение процессов в совместном режиме (interleaving) может привести к следующему эффекту:

производитель: register1 = counter (register1 = 5)

производитель: register1 = register1 + 1 (register1 = 6)

потребитель: register2 = counter (register2 = 5)

потребитель: register2 = register2 – 1 (register2 = 4)

производитель: counter = register1 (counter = 6)

потребитель: counter = register2 (counter = 4)

Таким образом, значение counter в итоге может оказаться равным 6 или 4, в то время как правильное значение counter равно 5.

Ситуация, при которой взаимодействующие процессы могут параллельно (одновременно) обращаться к общим данным, называется конкуренцией за общие данные (race condition).Для предотвращения подобных ситуаций процессы следует синхронизировать.

Синхронизация процессов по критическим секциям

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

Можно показать, что для решения проблемы критической секции необходимо и достаточно выполнение следующих трех условий:

  1. Взаимное исключение. Если некоторый процесс исполняет свою критическую секцию, то никакой другой процесс не должен в этот момент исполнять свою.
  2. Прогресс.Если в данный момент нет процессов, исполняющих критическую секцию, но есть несколько процессов, желающих начать исполнение критической секции, то выбор системой процесса, которому будет разрешен запуск критической секции, не может продолжаться бесконечно.
  3. Ограниченное ожидание.В системе должно существовать ограничение на число раз, которое процессам разрешено входить в свои критические секции, начиная от момента, когда некоторый процесс сделал запрос о входе в критическую секцию, и до момента, когда этот запрос удовлетворен.

При этом предполагается, что каждый процесс исполняется с ненулевой скоростью, но не делается никаких предположений о соотношении скоростей процессов.

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