【问题标题】:Calling Monitor.TryEnter without Monitor.Exit at timer elapsed callback shows unexpected behavior在计时器经过回调时调用 Monitor.TryEnter 而不使用 Monitor.Exit 会显示意外行为
【发布时间】:2026-01-26 00:20:07
【问题描述】:

这里的问题是:如果一个线程获得一个对象的独占锁——例如通过使用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
....

【问题讨论】:

    标签: c# .net multithreading timer locking


    【解决方案1】:

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

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

    【讨论】:

    • +1,我没有注意到操作成功是在同一个线程上:)
    【解决方案2】:

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

    这称为重入,通常用于递归调用。你使用它有点奇怪,仅此而已。

    如果获取对象排他锁的线程(例如通过使用 Monitor.Enter)被终止,是否会神奇地释放该对象上的排他锁?

    唉,不。

    这就是为什么您应该始终将 TryEnter() 与 try/finally 结合以执行 Exit()

    【讨论】:

    • +1 感谢你和 slake.. 这让我发疯了!我没有注意到操作成功只是同一个线程:)
    最近更新 更多