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

На основе инструкции «Test & Set»

Запрет прерываний не работает на многопроцессорных машинах!

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

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

Пример: Инструкцияxchg (Intel x86) меняет местами содержимое регистра и ячейки памяти. Вспомним, что значение регистра всегда является локальным для нити, а переменная в памяти может быть общей (глобальной) для нескольких нитей. После атомарной операции обмена значение из памяти оказывается в локальном контексте и может быть спокойно проанализировано

Будем называть такие инструкции «test&set» и считать, что в одно действие эта инструкция считывает содержимое ячейки памяти и записывает в нее 1.

Как это работает?

Пусть переменная value = 0.

void Lock::Acquire()

{

while(test&set(value) == 1); //Ждем

}

void Lock::Release() { value) = 0; } //Освобождаем

Недостаток: - занятое ожидание; в результате которого задерживается выполнение нити, захватившей Lock.

Можно ли устранить занятое ожидание? – Нет!

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

Применять можно, если минимизировать занятое ожидание, например, так:

Class Lock {

bool busy

int guard;

public:

Lock() { busy = false; guard = 0; }

void Acquire();

void Release();

};

void Lock::Acquire ()

{

while(test&set(quard));

if(busy) {

// поставить в очередь к этому Lock

schedule(&gaurd); // передать управление другому

// и сбросить guard

}

else {

busy = true;

guard = 0;

}

}

void Lock::Release ()

{

while(test&set(quard));

if(очередь не пуста) {

// исключить первого из очереди

// поставить в очередь готовых

}

else

busy = false;

guard = 0;

}

Резюме

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

Одна из первых реализаций взаимного исключения без занятого ожидания реализована в UNIX в виде системных вызовов(sleep и wakeup).

Особенности:

  • wakeup доsleep эквивалентен «пустому» действию и факт его выполнения не регистрируется

  • Если ждут несколько, то wakeup “разбудит ”всех сразу.

Написание параллельных программ – задача не простая, поэтому нужны «правильные» примитивы облегчающие синхронизацию.

Семафоры

Семафоры – примитивы для синхронизации параллельных процессов, предложенные (Edsger Dijkstra) в середине 60-х. Являлись основным примитивом синхронизации в раннемUNIXе. Сейчас используются вOS/2, Windows NT, UNIX.

С семафором связана целочисленная переменная, очередь ожидающих на семафоре процессов и две функции:

  • P (proberen)- атомарная операция, которая останавливает работу процесса(нити) до тех пор, пока переменная семафора станет положительной, затем уменьшает переменную на 1. Является аналогом операции wait.

  • V (verhogen)- атомарная операция, которая увеличивает переменную семафора на 1 и будит один процесс из очереди к семафору, если кто-то ждет. Является аналогом операцииsignal.

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