
- •Пояснительная записка
- •Задание
- •Оглавление
- •1 Введение
- •2.Основная часть
- •2.1 Описание методологии мониторов
- •2.2 Синтаксис и семантика
- •2.3 Взаимное исключение
- •2.4 Условные переменные
- •2.5 Дисциплины сигнализации
- •2.6 Взаимоисключительность
- •2.7 Применение
- •3 Заключение Список используемых источников
2.3 Взаимное исключение
Синхронизацию проще всего понять и запрограммировать, если взаимное исключение и условная синхронизация выполняются разными способами. Лучше всего, если взаимное исключение происходит неявно, чем автоматически устраняется взаимное влияние. Кроме того, программы легче читать, поскольку в них нет явных протоколов входа в критические секции и выхода из них.
В отличие от взаимного исключения, условную синхронизацию нужно программировать явно, поскольку разные программы требуют различных условий синхронизации. Хотя зачастую проще синхронизировать с помощью логических условий, как в операторах await, низкоуровневые механизмы можно реализовать намного эффективнее. Они позволяют программисту более точно управлять порядком выполнения программы, что помогает в решении проблем распределения ресурсов и планирования.
В соответствии с этими замечаниями взаимное исключение в мониторах обеспечивается неявно, а условная синхронизация программируется с помощью так называемых условных переменных.
Внешний процесс вызывает процедуру монитора. Пока некоторый процесс выполняет операторы процедуры, она активна. В любой момент времени может быть активным только один экземпляр только одной процедуры монитора, т.е. одновременно не могут быть активными ни два вызова разных процедур, ни два вызова одной и той же процедуры.
Процедуры мониторов по определению выполняются со взаимным исключением. Оно обеспечивается реализацией языка, библиотекой или операционной системой, но не программистом, использующим мониторы. На практике взаимное исключение в языках и библиотеках реализуется с помощью блокировок и семафоров, в однопроцессорных операционных системах — запрета внешних прерываний, а в многопроцессорных операционных системах — межпроцессорных блокировок и запрета прерываний на уровне процессора.
2.4 Условные переменные
Условная переменная используется для приостановки работы процесса, безопасное выполнение которого невозможно до перехода монитора в состояние, удовлетворяющее некоторому логическому условию. Условные переменные также применяются для запуска приостановленных процессов, когда условие становится истинным. Условная переменная объявляется следующим образом:
cond cv;
Таким образом, cond - это новый тип данных. Массив условных переменных объявляется, как обычно, указанием интервала индексов после имени переменной. Условные переменные можно объявлять и использовать только в пределах мониторов.
Значением условной переменной cv является очередь приостановленных процессов (очередь задержки). Вначале она пуста. Программист не может напрямую обращаться к значению переменной cv. Вместо этого он получает косвенный доступ к очереди с помощью нескольких специальных операций, описанных ниже.
Процесс может запросить состояние условной переменной с помощью вызова:
empty(cv);
Если очередь переменной cv пуста, эта функция возвращает значение "истина", иначе — "ложь".
Процесс блокируется на условной переменной cv с помощью вызова:
wait(cv);
Выполнение операции wait заставляет работающий процесс задержаться в конце очереди переменной cv. Чтобы другой процесс мог в конце концов войти в монитор для запуска приостановленного процесса, выполнение операции wait отбирает у процесса, вызвавшего ее, исключительный доступ к монитору.
Процессы, заблокированные на условных переменных, запускаются операторами signal. При выполнении вызова проверяется очередь задержки переменной cv. Если она пуста, никакие действия не производятся. Однако, если приостановленные процессы есть, оператор signal запускает процесс вначале очереди. Таким образом, операции wait и signal обеспечивают порядок сигнализации FIFO: процессы приостанавливаются в порядке вызовов операции wait, а запускаются в порядке вызовов операции signal.
Следующий монитор использует условные переменные для реализации канала между процессами, который может хранить одномоментно только одно целочисленное значение.
monitor channel {
int contents
boolean full := false
condition snd
condition rcv
function send(int message) {
while full do wait(rcv) // семантика Mesa: см.ниже
contents := message
full := true
notify(snd)
}
function receive() {
var int received
while not full do wait(snd) // семантика Mesa: см.ниже
received := contents
full := false
notify(rcv)
return received
}
}
Заметьте, что, поскольку ожидание условия отпускает блокировку, ожидающий процесс должен гарантировать соблюдение инварианта перед тем, как начать ожидание. В примере выше, то же справедливо и для оповещения.
Примерная реализация условной переменной:
conditionVariable {
int queueSize = 0;
mutex lock;
semaphore waiting;
wait() {
lock.acquire();
queueSize++;
lock.release();
waiting.down();
}
signal() {
lock.acquire();
while (queueSize > 0){
queueSize--;
waiting.up();
}
lock.release();
}
}