Задание №2
Для заданной группы вычислительных процессов организовать доступ к критической секции с использованием (по указанию преподавателя): блокирующей переменной.
Объяснить достоинства и недостатки каждого из методов взаимного исключения или организации доступа к разделяемым ресурсам. Привести примеры использования объектов синхронизации в Windows 2000/XP
Классической задачей при параллельном программировании является задача критической секции".
В этой задаче п процессов многократно выполняют сначала критическую, а затем некритическую секцию кода.
Критической секции соответствует протокол входа, а за ней следует протокол выхода. Итак, процесс имеет вид
process CS[i = 1 to n]
{ while (true) {
[протокол входа];
[критическая секция];
[протокол выхода];
[некритическая секция]; } }
Протоколы входа должны удовлетворять следующим свойствам.
1. "Взаимное исключение": й любой момент только один процесс может
выполнять свою критическую секцию.
2. "Отсутствие взаимной блокировки": если несколько процессов пытаются войти в свои критические секции; хотя бы один это осуществит.
3. "Отсутствие излишних задержек"; если процесс хочет войти в сбою критическую секцию, а другие выполняют некритические секции или завершены, то процессу разрешается вход в упомянутую критическую секцию.
4. "Возможность входа": процесс, который пытается войти в критическую секцию, когда-нибудь это сделает.
Свойство "взаимное исключение" нарушается, если два процесса находятся в своих критических секциях.
Свойство "отсутствие излишних задержек" нарушается, если единственный процесс, пытающийся войти в критическую секцию, не может этого сделать.
Пример. Пусть inl и in2 — логические переменные. Процесс SI (S2) находится в критической секции, в то время как inl (in.2) присваивается "истина".
Для взаимного исключения требуется истинность состояния MUTEX: -i(inl^in2).
Программа для критической секции (крупномодульное программирование)
bool inl = false, in.2 = false;
## MUTEX : -.(inl Л in2) — глобальный инвариант
1) process CS1 {
2) while (true) {
3) < await (!in2) inl = true; > # вход
4) [критическая секция];
5) inl = false; # выход
6) [некритическая секция];
7) }
8) }
9) process CS2 {
10) while (true) {
11) < await (!inl) in2 = true; > # вход
12) [критическая секция];
13) in2 = false; # выход
14) [некритическая секция];
15) }
16)}
14.2. Активные блокировки
Можно рассмотреть произвольное число п процессов, но использовать одну логическую переменную (вместо п логических переменных в каждом процессе).
Пусть lock — логическая переменная. Если один из процессов находится в критической секции, то в предыдущей программе inl V in2 = true. Пусть lock принимает,значение true, если хотя бы какой-либо процесс находится в критической секции, а если нет процес-сов в критической секции, то lock = false. Проиллюстрируем применение этой переменной для двух процессов (п = 2).
Замечание 1. Инвариант, рассмотренный выше (а именно -i(inl ^ in2)), нельзя сформулировать при помощи переменной lock. Однако использо-вание lock позволяет поддерживать этот инвариант; это вытекает из сле-дующего примера:
bool lock = false; process CS1 { while (true) {
< await (!lock) lock = true; > # если !lock, то вход . .
# разрешен: входим, [критическая секция];
lock = false; # выходим из критической секции и сразу
# устанавливаем lock = false [некритическая секция];
}}
process CS2 { while (true) {
< await (!lock) lock = true; > # если ilock, то вход
# разрешен: входим... [критическая секция];
lock = false; # выходим из критической секции и сразу
# устанавливаем lock = false [некритическая секция];
}}
Замечание 2. Использование логической переменной lock при произвольном числе процессов аналогично.
14.3. Протокол "проверить-установить"
Протокол "проверить-установить" (Test and Set, кратко — TS) получает в качестве аргумента переменную lock, в неделимом действии присваивает ей значение true, а затем возвращает предыдущее сохраненное значение переменной lock. Это описывается так:
bool TS(bool lock) {
(bool initial = lock; # сохраняем начальное значение lock = true; # ставим lock внутри неделимого действия return initial;) # возвращаем предыдущее значение
Приведем программу с использованием инструкции TS. Такое ее использование называется циклической блокировкой.
Критические секции на основе протокола "проверить-установить" (циклическая блокировка).
1) bool lock = false;
2) process CS[i = 1 to n] {
3) while (true) {
4) while (TS(lock)) skip; # протокол входа
5) [критическая секция];
# на основании свойства TS здесь lock == true
6) lock = false; # восстанавливаем возможность доступа
Ф к критическим секциям
7) [некритическая секция];
8) }
9)}
Свойства этой программы следующие:
— взаимное исключение выполнено, ибо если lock = false и несколько процессов хотят войти в критическую секцию, то только один войдет в нее, и произойдет присваивание lock = true (другие не войдут);
— отсутствие взаимной блокировки выполнено: если оба процесса находятся во входных протоколах, то lock имеет значение false, и лишь один из них, войдя в критическую секцию, сразу изменит (с помощью TS) значение переменной lock на true (второй процесс войти в критическую секцию не сможет);
— излишних задержек не возникает: если оба процесса находятся вне критических секций, то один из них может войти;
если используется стратегия планирования, справедливая в слабом смысле, то возможность входа не гарантируется (возможен поток значений false у переменной lock как раз в моменты проверок этого значения процессом); однако если стратегия планирования справедлива в сильном смысле, то возможность входа гарантируется.
