Вызов Monitor.TryEnter без Monitor.Exit по истечении таймера обратного вызова показывает неожиданное поведение - PullRequest
1 голос
/ 26 марта 2012

Вопрос здесь следующий: если поток, который получает монопольную блокировку для объекта - например, с помощью Monitor.Enter, - прерывается, освобождает ли это волшебным образом монопольную блокировку для этого объекта?если это правда, то предположим, что мы вызываем Monitor.Exit из другого потока - потому что мы предполагали, что объект заблокирован - разве это не выбросит исключение типа SynchronizationLockException?

Я неуверен, что это ожидаемое поведение или ошибка ...

Чтобы продемонстрировать проблему, я создал простой фрагмент кода, который вызывает Monitor.TryEnter без вызова Monitor.Exit at System.Timers.Timer.Elapsed обратный вызов события "Обратите внимание, что класс Timers.Timer внутренне использует ThreadPool для запуска обработчика события обратного вызова, и он may каждый раз вызывает прошедший обратный вызов в другом потоке из пула .. "

class Foo
{
    private System.Timers.Timer _timer = new System.Timers.Timer(2000);//every 2s

    private static readonly object _locker = new object();//static locker object

    public Foo()
    {
        _timer.Elapsed += delegate
        {
            if (Monitor.TryEnter(_locker))//acquiring the lock without calling Exit.. 
            {
                Console.WriteLine(string.Format("Access Succeed!!, Thread Id {0}",
                    Thread.CurrentThread.ManagedThreadId));

                Thread.Sleep(6000);//simulate a work for 6 seconds
            }
            else
            {
                Console.WriteLine(string.Format("Unable to access to locker method within locker object, Thread Id {0}",
                    Thread.CurrentThread.ManagedThreadId));
            }
        };

        _timer.Start();
    }
}


//to test: just initialize a new instance of foo class
Foo foo = new Foo();
//blocks until application is terminated..
Thread.Sleep(Timeout.Infinite);

Результаты в окне вывода показывают:

Access Succeed!!, Thread Id 11
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Access Succeed!!, Thread Id 11
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
Access Succeed!!, Thread Id 11
Unable to access to locker method within locker object, Thread Id 12
Unable to access to locker method within locker object, Thread Id 12
....

Ответы [ 2 ]

2 голосов
/ 26 марта 2012

Вы видите, что множественные вызовы TryEnter из одного потока будут успешными.

Это называется повторным входом и обычно используется для рекурсивных вызовов. Вы используете это немного странно, вот и все.

Если поток, который получает эксклюзивную блокировку для объекта, например, с помощью Monitor.Enter, завершается, освобождает ли это магическим образом исключительную блокировку для этого объекта?

Увы, нет.

Вот почему вы всегда должны комбинировать TryEnter() с попыткой / наконец, чтобы выполнить Exit()

2 голосов
/ 26 марта 2012

Здесь нет прерываний потоков.
Скорее, они возвращаются в пул потоков, только для повторного использования в более поздних событиях таймера.

Monitor.Enter является повторно входящим;Если дважды ввести блокировку в одном потоке, блокирование не будет.

...