іж виконанням операцій перевірки і установки блокуючої змінної. Пояснимо це. Нехай в результаті перевірки змінної потік визначив, що ресурс вільний, але відразу після цього, не встигнувши встановити змінну в 0, був перерваний. За час його призупинення інший потік зайняв ресурс, увійшов у свою критичну секцію, але також був перерваний, не завершивши роботи з ресурсом. Коли управління було повернуто першому потоку, він, вважаючи ресурс вільним, встановив ознака зайнятості і почав виконувати свою критичну секцію.
Таким чином, було порушено принцип взаємного виключення, що потенційно може призвести до небажаних наслідків. Щоб уникнути таких ситуацій в системі команд багатьох комп'ютерів передбачена єдина, неподільна команда аналізу і присвоєння значення логічної змінної (наприклад, команди ВТС, ВТК і ВТ5 процесора Реntium). При відсутності такої команди в процесорі відповідні дії повинні реалізовуватися спеціальними системними примітивами (примітив - базова функція ОС), які б забороняли переривання протягом всієї операції перевірки і установки. Реалізація взаємного виключення описаним вище способом має істотний недолік: протягом часу, коли один потік знаходиться в критичній секції, інший потік, якому потрібно той же ресурс, отримавши доступ до процесора, буде безупинно опитувати блокирующую змінну, марно витрачаючи виділяється йому процесорний час, який могло б бути використане для виконання якого-небудь іншого потоку. Для усунення цього недоліку в багатьох ОС передбачаються спеціальні системні виклики для роботи з критичними секціями.
Семафори (semaphore) - це основний метод синхронізації. Він, по суті, є найбільш загальним методом синхронізації процесів. У класичному визначенні семафор являє собою цілу змінну, значення якої більше нуля, тобто просто лічильник. Зазвичай семафор инициализируется на початку програми 0 або 1. Семафори, які можуть приймати лише значення 0 і 1, називаються двійковими. Над семафорами визначені дві операції - signal і wait . Операція signal збільшує значення семафора на 1, а викликав її процес продовжує свою роботу. Операція wait призводить до різних результатів, залежно від поточного значення семафора. Якщо його значення більше 0, воно зменшується на 1, і процес, що викликав операцію wait , може продовжуватися. Якщо семафор має значення 0, то процес, що викликав операцію wait , призупиняється (ставиться в чергу до семафора) до тих пір, поки значення відповідного семафора не збільшиться іншим процесом за допомогою операції signal . Тільки після цього операція wait припиненого процесу завершується (зі зменшенням значення семафора), а призупинений процес триває. Важливо, що перевірка і зменшення значення семафора в операції wait виконуються за один крок. Операційна система не може перервати виконання операції wait між перевіркою і зменшенням значення.
Операція wait для семафора має таке ж функціональне значення, що і інструкція test_and_set . Якщо кілька процесів чекають одного і того ж семафора, то після виконання операції signal тільки один з них може продовжити свій розвиток. Залежно від реалізації процеси можуть чекати в черзі, впорядкованої або за принципом FIFO (Firstln, FirstOut - першим увійшов, першим вийшов), або відповідно до пріоритетів, або вибиратися випадковим чином. Назви керуючої структури семафор та операцій signal і wait мають очевидну мнемонічне значення. У літературі замість signal і wait застосовуються й інші назви з тим же самим функціональним змістом. За допомогою семафорів проблема захисту ресурсів вирішується таким чином:
program sem_example (* захист ресурсу *) var P1: semaphore begin P1:=1; cobegin while true do (* нескінченний цикл *) begin (* процес А *) wait (P1); (* Захищений ресурс *) signal (P1);... end ; (* Процес А *) while true do (* нескінченний цикл *) begin (* процес В *) wait (Pl); (* Захищений ресурс *) signa l (Pl);... end; (* процес В *) coend ; end . (* Sem_example *) Семафор гарантує, що два процеси можуть отримати доступ до захищеного ресурсу тільки по черзі.
При цьому не створюється ніяких додаткових зв'язків - якщо один проц...