Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СПОС_Конспект.doc
Скачиваний:
39
Добавлен:
02.05.2019
Размер:
1.13 Mб
Скачать

5. 2. 2 Критичні секції та блокування Поняття критичної секції

Розглянемо використання простішої ідеї для вирішення проблеми змагань.

Неважко помітити, як джерелом нашої помилки є те, що зовні найпростіша операція покладання грошей на рахунок насправді розпадається на кілька операцій, при цьому завжди залишається шанс втручання між ними якогось іншого потоку. У цьому випадку кажуть, що операція не є атомарною.

Звідси випливає, що розв’язанням проблеми змагання є перетворення фрагменту коду, який спричиняє проблему, в атомарну операцію, тобто в таку, котра гарантовано виконуватиметься цілковито без втручання інших потоків. Такий фрагмент коду називають критичною секцією (critical section):

//початок критичної секції

total_amount=total_amount+new_amount;

//кінець критичної секції

Тепер, коли два потоки візьмуться виконувати код критичної секції одночасно, той з них, що почав першим, виконає весь її код цілком до того, як другий почне своє виконання (другий потоік чекатиме, поки перший не закінчить виконання коду критичної секції). У результаті підсумку гарантовано матимемо в нашій програмі послідовність подій за варіантом 2, і змагання не відбудеться ніколи.

Розглянемо властивості, які повинна мати критична секція.

  • Взаємного виключення (mutual exlusion): у конкретний момент часу код критичної секції може виконувати тільки один потік.

  • Прогесу: якщо кілька потоків запрсили вхід у критичну секцію, один із них повинен обов’язково у неї ввійти (вони не можуть всі заблокувати один одного).

  • Обмеженості очікування: процес, що намагається ввійти у критичну секцію, рано чи пізно обов’язково в неї ввійде.

Залишається відповісти на далеко не просте запитання: “Як нам змусити ситему сприймати кілька операцій як одну атомарну операцію?”

Найпростішим розв’язанням такої задачі було б заборонити переривання на час виконання критичної секції. Такий підхід, хоча й розв’язує задачу в принципі, на практиці не може бути застосовуваний, оскільки внаслідок зациклення аьо авірії програми у критичній секції вся система може залишитися із заблокованими перериваннями, а отже, у непрацездатному стані.

Блокування

Раціональнішим розв’язанням є використання блокувань (locks). Блокування – це механізм, який не дозволяє більш як одному потокові виконувати код критичної секції. Використання блокування зводиться до двох дій: запрвадження (заблокування, функція acquire_lock()) і зняття блокування (розблокування, функція release_lock()). У разі заблокування перевіряють, чи не було воно вже зроблено іншим потоком, і якщо це так, цей потік переходить у стан очікування, інакше він запрваджує блокування і входить у критичну секцію. Після виходу із критичної секції потік знімає блокування.

acquire_lock(lock);

//критична секція

release_lock(lock);

Так реалізовують властивість взаємного виключення, звідси походить інша назва для блокування – мютекс (mutex, скорочення від mutual exclusion).

Проблеми із реалізацією блокувань

Розглянемо наївну реалізацію критичної секції із використанням змінних блокування. З кожною критичною секцією пов’язують цілочислову змінну, якій присвоюють одиницю під час заблокування і нуль – після разблокування. Ось який приблизно вигляд має код такої реалізації:

int lock=0;

void aquire_lock(int lock)

{

//якщо блокування не має (lock==0), запровалити його (задати lock=1)

//і вийти – ми ввійшли у критичну секцію

//інакше чекати, поки блокування не знімуть

while(lock!=0); //(1)

lock=1; //(2)

}

void release_lock(int lock)

{

//зняти блокування

lock=0;

}

Головна проблема цієї реалізації полягає в тому, що що перевірка значення змінної блокування (1) та її зміна (2) не є частинами однієї атомарної операції. Коли інший потоік перевірить значення змінної між цима двома операціями і виявить, що вона дорівнює 0, два потоки можуть опинитися у критичній секції одночасно.