Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
26
Добавлен:
17.04.2013
Размер:
65.54 Кб
Скачать

7

СПО Лекция 7[28.02.17]

Лекция 7: Мониторы и условные переменные

  • Монитор и условные переменные

  • Задача о производителе и потребителе

Монитор и переменные условия

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

Мониторы предложены с целью разделить эти задачи и упростить программирование. Монитор содержит общие переменные, разделяемые процессами и функции доступа к общим переменным. Тело функции монитора является критической секцией и должно быть защищено от повторного входа. Другими словами, только один процесс может выполнять функцию монитора.

Первоначально (Хоар, Бринч Хансен) определяли монитор как конструкцию в языке программирования, при этом lock/unlock подставлялись в ходе компиляции соответственно в начале и на выходе из функций монитора. Во многих ОСOS/2, Windows NT, Solaris мониторы строятся на основе явного вызова примитивов взаимного.

Рассмотрим пример взаимодействия двух процессов – передача (прием) данных через кольцевой буфер.

class Buffer {

int buf[N];

int head, tail

public:

Buffer() { head = tail = 0; }

void Put(int value);

void Get(int& value);

};

void Buffer::Put(int value)

{

while( (tail+1)%N == head); // пока буфер полон - ждать

// или while( (tail+1)%N == head) Sleep(1);

buf[tail] = value; // Разместить данные в буфере

tail = (tail+1)%N;

}

void Buffer::Get(int& value)

{

while( tail == head); // пока буфер пуст - ждать

// или while( tail == head) Sleep(1);

value = buf[head]; // Взять данные из буфера

head = (head +1)%N;

}

Приведенное решение справедливо только для двух процессов: один передает данные, а другой принимает. Процессы используют разные функции и модифицируют разные переменные: один – head, а другой –tail.

Если допускается существование нескольких передающих или нескольких читающих процессов, то операции чтения и записи должны быть защищены критической секцией. В определение класса необходимо добавить переменную MUTEX lock, а в начало и в конец функцийPut иGet добавить соответственно захват (lock.Acquire()) и освобождение (lock.Release()) критической секции.

Однако, это решение не верно. Проблема в том, что ожидание внутри критической секции в данном случае приводит к блокировке. Например, если буфер пуст, то мы ждем в цикле while внутри критической секции и другой процесс не сможет выполнить функциюPut, поскольку критическая секция занята.

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

Для переменных условия определены функции: wait(suspend), signal(resume), broadcast.

  • wait выход из критической секции, ожидание, возврат в критическую секцию

  • signal будит одного ожидающего, если кто-то ждет

  • broadcast будит всех ожидающих, если кто-то ждет

Получаем следующее решение:

class Buffer {

MUTEX lock

CONDITION full,empty;

int buf[N];

int head, tail

public:

Buffer() { head = tail = 0; }

void Put(int value);

void Get(int& value);

};

void Buffer::Put(int value)

{

lock.Acquire();

while( (tail+1)%N == head) full.wait(lock); // ждать

buf[tail] = value; // Разместить данные в буфере

tail = (tail+1)%N;

empty.signal();

lock.Release();

}

void Buffer::Get(int& value)

{

lock.Acquire();

while( tail == head) empty.wait(lock); // ждать

value = buf[head]; // Взять данные из буфера

head = (head +1)%N;

full.signal();

lock.Release();

}

Два стиля мониторов: Mesa vs. Hoar

Различие в том, как осуществляется возврат в критическую секцию после wait.

  • MESA (реально применяется): послеsignal не происходит принудительного переключения контекста, разбуженный просто ставится в очередь готовых и должен вновь проверять право на вход в монитор!

  • HOAR (описан в учебниках): послеsignal происходит принудительное переключения контекста, управление получает разбуженный процесс и только в момент его выхода из монитора процесс, выполнившийsignal, получает возможность продолжить выполнение.

  • Рассмотренный пример справедлив для обоих стилей. Если заменить while наif, то решение останется справедливым для монитора Хоара, но не будет работать для монитора стиляMESA.

  • wait иsignal не эквивалентны семафорным примитивам - нет счетчика, поэтомуsignal, выполненный раньшеwait эквивалентен пустому оператору.

Соседние файлы в папке вар1