Вопрос об использовании объекта 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);
    }
}