Невозможно, чтобы два (или более) потока получили блокировку одновременно. Например, существует несколько типов методов синхронизации:
Активное ожидание - спин блокировка
псевдокод:
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 должен давать вам возможность указывать максимальное количество параллельных потоков в критической секции семафора. Очевидно, что в многопоточных системах существует больше типов синхронизации (мьютекс, мониторы, двоичный семафор и т. Д.), Но они основаны на вышеуказанных идеях. Можно утверждать, что методы, использующие приостановку потоков, следует отдавать предпочтение активному ожиданию (поэтому процессор не теряется) - это не всегда правда. Когда поток приостанавливается - его заменяет дорогая операция, называемая переключением контекста. Однако разумно, когда время ожидания короткое (количество потоков ~ количество ядер).