Неможливо, щоб дві (або більше) ниток придбали замок одночасно. Наприклад, існує кілька типів методів синхронізації:
Активне очікування - відключення блокування
Псевдокод:
1. while ( xchg(lock, 1) == 1); - entry protocole
XCHG - приклад атомної роботи (існує в архітектурі x86), який спочатку встановлює нове значення для змінної "lock", а потім повертає старе значення. Атомний означає, що його неможливо перервати - у наведеному вище прикладі між встановленням нового значення та поверненням старого. Атомно - детермінований результат незалежно від того.
2. Your code
3. lock = 0; - exit protocol
Якщо блокування дорівнює 0, інша нитка може перейти до критичного розділу - поки цикл закінчується.
Призупинення нитки - наприклад, підрахунок семафору
Існує дві атомні операції, .Wait()
і у .Signal()
нас є ціла змінна давайте її назвати int currentValue
.
Wait():
if (currentValue > 0) currentValue -= 1;
else suspend current thread;
Signal():
If there exists thread suspended by semaphore wake up one of them
Else currentValue += 1;
Зараз вирішити проблему критичного розділу дуже просто:
Псевдокод:
mySemaphore.Wait();
do some operations - critical section
mySemaphore.Signal();
Зазвичай API вашої програмувальної нитки повинен надавати можливість задавати максимальні паралельні потоки в критичному розділі семафору. Очевидно, що існує багато типів синхронізації у багатопотокових системах (мютекс, монітори, бінарний семафор тощо), але вони ґрунтуються на наведених вище ідеях. Можна стверджувати, що методи, які використовують призупинення потоків, слід віддавати перевагу активному очікуванню (тому процесор не витрачається даремно) - це не завжди правда. Коли нитка призупинена - її місце займає дорога операція під назвою контекстний комутатор. Однак розумно, коли час очікування короткий (кількість ниток ~ кількість ядер).