Програмно-апаратна реалізація блокувань



Заблокування і розблокування із використанням інструкції TSL (тут буде використано псевдокод, у якому TSL(lock) відповідатиме виконанню операції TSL над ділянкою пам’яті, що відповідає змінній lock) можна реалізувати так:

void acquire_lock(int lock) {

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

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

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

while (TSL(lock) !=0);

}

void release_lock (int lock) {

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

lock=0;

}

Головним недоліком описаної технології є те, що потік повинен постійно перевіряти в циклі, чи не зняли блокування. Таку ситуацію називають активним очікуванням (busy waiting), а таке блокування – спін-блокуванням (spinlock). Використання циклу під час активного очікування завантажує процесор і допустиме тільки упродовж короткого часу (це справедливо для одно процесорних систем, у багатопроцесорних використання спін-блокувань може бути виправдане тому, що під час виконання циклу активного очікування одним процесором інші можуть продовжувати свою роботу).

Альтернативою активному очікуванню є операція призупинення потоку (thread blocking, sleep). При цьому потік за умови блокування негайно або через якийсь час переходить зі стану виконання у стан очікування, передаючи процесор іншому потокові. Для того щоб реалізувати критичну секцію за допомогою цієї операції, потрібно доповнити її операцією виведення потоку зі стану очікування (поновленням потоку, wakeup). Робота із критичною секцією в цьому разі буде мати такий вигляд:

int lockguard=0; // блокування для критичних секцій усередині

// acquire_lock і release_lock

void acquire_lock(int lock) {

// перевірити, чи безпечно виконувати цю дію

while (TSL(lockguard) !=0);

// якщо блокування запроваджене – призупинити потік

if (lock==1) sleep();

// інакше заблокувати й увійти у критичну секцію

else lock = 1;

// дає можливість іншим потокам виконувати цю дію

// цей код повинен виконуватися завжди у разі завершення операції

lockguard = 0;

}

void release_lock(int lock) {

// перевірити на безпеку виконання цієї дії

while (TSL(lockguard) !=0);

// розбудити один із призупинених потоків, якщо вони є

if (waiting_threads()) wakeup(some_thread);

// якщо призупинені потоки відсутні – зняти блокування

else lock = 0;

// дає можливість іншим потокам виконувати цю дію

// цей код повинен виконуватися завжди у разі завершення операції

lockguard = 0;

}

Виділимо деякі особливості цього алгоритму.

· Інструкцію TSL використовують тільки для забезпечення атомарності виконання операцій acquire_lock і release_lock (для перевірки безпеки цих операцій використовують змінну lockguard). У результаті потік перебуватиме у стані активного очікування не довше, ніж потрібно іншим потокам для виконання цих операцій.

· За наявності блокування потік буде призупинено. На практиці зазвичай із блокуванням lock пов’язують чергу очікування, в яку й буде поміщено цей потік.

· У разі спроби розблокування, якщо відповідна черга очікування не порожня, виконання одного із призупинених потоків буде продовжене і блокування не зняте. Реальне розблокування відбудеться тільки тоді, коли черга очікування порожня.

· Псевдокод неповністю відображає ту ситуацію, що змінній lockguard після виходу з acquire_lock і release_lock повинне бути присвоєне значення 0 завжди (навіть під час перемикання контексту). У противному разі інші потоки не змогли б виконувати ці операції.

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

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

Механізми синхронізації ОС.

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

Сучасні ОС надають широкий набір готових механізмів синхронізації.


Дата добавления: 2018-04-05; просмотров: 355; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!