Вопрос об использовании объекта Monitor.TryEnter и блокировки
Рассмотрим следующую функцию, которая реализует неблокирующий доступ только к одному потоку.
public bool TryCancelGroup()
{
if (Monitor.TryEnter(_locked))
{
if (_locked == false)
{
_locked = true;
try
{
// do something
}
catch (Exception ex)
{
_locked = false;
}
finally
{
Monitor.Exit(_locked);
}
}
return _locked;
}
else
{
return false;
}
}
И вот как определяется переменная _locked
.
bool _locked = false;
Теперь, когда программа достигает Monitor.Exit(_locked);
, она выдает сообщение System.Threading.SynchronizationLockException
, в котором говорилось, что _блокированная переменная ранее не синхронизировалась.
Все это работало до того, когда переменная _locked была определена как объект
object _locked = new object();
Когда я изменил его на bool, чтобы использовать его как логический флаг, я начал получать это исключение.
Ответы
Ответ 1
Причина, по которой методы Monitor
принимают параметр System.Object
. Когда вы проходите в bool
, для преобразования в Object
требуется поле. Операция box создает новое значение System.Object
для каждого вызова. Поэтому методы TryEnter
и Exit
видят разные объекты и результаты в исключении.
Когда _locked
было напечатано на Object
, не было необходимости в ящике. Следовательно, методы TryEnter
и Exit
видят один и тот же объект и могут функционировать корректно.
Несколько других комментариев о коде
- TryEnter должен быть сопряжен с Exit во всех случаях и для разумного использования, вызов Exit должен быть в блоке finally. В противном случае вы вызываете сценарий взаимоблокировки.
- Переменная
_locked
установлена только в false
перед лицом исключения. Если выполнение не создает исключение, оно останется истинным, и ни один поток никогда не войдет в блок if
.
Ответ 2
Установка времени ожидания на мониторе на 0 может помочь реализовать желаемое поведение. Используйте глобально объявленный объект для блокировки.
static object mylock = new object();
....
if (Monitor.TryEnter(mylock, 0))
{
try
{
// Do work
}
finally
{
Monitor.Exit(mylock);
}
}