在没有Monitor.Exit的情况下调用Monitor.TryEnter在计时器过去的回调中显示意外行为

时间:2012-03-26 17:58:36

标签: c# .net multithreading timer locking

这里的问题是:如果获取对象的排他锁的线程 - 例如通过使用Monitor.Enter - 被终止,那是否会神奇地释放该对象的排他锁?如果这是真的,那么假设我们从另一个线程调用Monitor.Exit - 因为我们假设该对象被锁定 - 这不会抛出SynchronizationLockException类型的异常吗?

我不确定这是预期的行为还是错误......

为了演示此问题,我创建了一段简单的代码,在Monitor.TryEnter事件回调时调用System.Timers.Timer.Elapsed而不调用Monitor.Exit“请注意Timers.Timer类在内部使用ThreadPool运行回调事件处理程序, 可能 每次在与池不同的线程上调用已过去的回调。“

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 个答案:

答案 0 :(得分:2)

这里没有线程终止。
相反,它们会返回到线程池,只能被以后的计时器事件重用。

Monitor.Enter是可重入的;在同一个线程上输入两次锁定不会阻止。

答案 1 :(得分:2)

您所看到的是,从相同线程多次调用TryEnter将成功。

这称为re-entrancy,通常用于递归调用。你使用它有点奇怪,就是这样。

  

如果获取对象的独占锁的线程 - 例如通过使用Monitor.Enter-终止,那么这会不可避免地释放该对象的独占锁吗?

唉,不,不。

这就是为什么你应该总是将TryEnter()与try / finally结合起来执行Exit()