Взаимное исключение:безопасно ли это?
-
23-08-2019 - |
Вопрос
Действительно ли этот шаблон взаимного исключения так безопасен, как я думаю?Если да, то как вы это называете?
lock (_lock) {
if (_flag) return;
else _flag = true;
}
try {
//critical code...
}
finally {
_flag = false;
}
Я хочу обеспечить критическую секцию, но без скопления других потоков, ожидающих получения блокировки.Очевидно, я слежу за тем, чтобы флаг больше нигде не был установлен.Есть ли лучший способ?
Решение
Нет, это небезопасно.Если вы хотите обеспечить взаимоисключающие действия без блокировки, вы можете использовать Monitor.Попробуйте ввести:
if (Monitor.TryEnter(lockObj, 0)) {
// got the lock !
try {
// code
}
finally { // release the lock
Monitor.Exit(lockObj);
}
}
Другие советы
Вы смотрели на Monitor.TryEnter
?
Корректность вашего шаблона взаимного исключения зависит от того, является ли присвоение _flag=false атомарным.Представьте, что бы произошло, если бы задание могло быть прервано другим потоком.Если промежуточные результаты задания могут быть интерпретированы тестом как false, одно задание может привести к тому, что несколько потоков войдут в критическую секцию.
Корректность шаблона взаимного исключения также зависит от отсутствия оптимизаций в компиляторе, которые могут изменить порядок инструкций.Представьте себе "умный" компилятор, который переместил бы присваивание _flag=false вверх, потому что _flag не упоминается в промежуточном коде (а промежуточный код не генерирует исключений).Затем компилятор мог бы оптимизировать часть в разделе блокировки для чтения
if(_flag) return;
Оба примера того, почему паттерн может дать сбой, носят весьма умозрительный характер, и я думаю, что вы можете с уверенностью предположить, что он работает.Однако при наличии другого варианта, который работает по мере необходимости, вам лучше использовать его (см. Другие сообщения).Если в том же коде есть другие разработчики, им не нужно рассматривать, работает ли шаблон.
Не было бы простого lock(Object)
заявление работает?За кулисами это создает Monitor
и критический раздел внутри try... finally
блок.
private static readonly Object lockMe = new Object();
lock(lockMe)
{
// critical code
}