Monitor.Enter 可以抛出异常吗?
Posted
技术标签:
【中文标题】Monitor.Enter 可以抛出异常吗?【英文标题】:Can Monitor.Enter throw an exception? 【发布时间】:2012-02-09 01:05:55 【问题描述】:Monitor.Enter
可以抛出任何异常。我正在做代码审查,发现 Monitor.Enter
在 try 块之前。你看到in有什么问题吗?
Monitor.Enter(...)
try
...
finally
Monitor.Exit(..)
【问题讨论】:
在 try 块中包含 Enter() 是一个错误。当 Enter() 可能失败时,您将调用 Exit()。您只需要对在 Enter() 调用之后、在 进入try 块之前引发的异常感到痛心。这实际上是 x64 抖动中的一个错误:bluebytesoftware.com/blog/2007/01/30/… 也是新 4.0 Monitor.Enter(object, ref bool) 重载背后的动机。 【参考方案1】:这是正确的模式,无论Enter()
是否抛出(可以抛出)。
只有在对Enter()
的调用成功后,您的代码才有责任调用Exit()
。
假设对Enter()
的调用失败。那么调用对应的Exit()
是完全不正确的,只会让事情变得更糟。所以Enter()
必须在try 块之外(之前)。
【讨论】:
【参考方案2】:Hans Passant 的评论当然是正确的。如果Monitor.Enter
在获取锁之前抛出了,那么你确实不想要 finally 运行。如果它抛出 after 获取锁并且 after 进入尝试,则释放锁。 (稍后会详细介绍。)但是如果在获取锁之后但在进入尝试之前发生抛出,那么锁将永远不会被清除。
This is a rare but possible situation.
在 C# 4 中,我们更改了 lock 语句的代码生成,以便监视器输入在 try 中。这确保了如果在获取锁后发生异常,锁总是被释放。但是,请注意这可能仍然是错误的。如果在获取锁之后发生了某些事情,那么锁所保护的任何非原子突变都可能是半完成的,然后 finally 块解锁锁并允许访问不一致的状态!这里的根本问题是,你一开始就不应该把锁扔进锁里。
更多讨论请见my article about the issue。
【讨论】:
我很想得到一个更通用的机制,也适用于自定义Enter/Leave
对。当前常用的using
语句与旧的lock 语句存在相同的问题。
@CodeInChaos:但不同之处在于,如果在错误的地方抛出异常并且分配的非托管资源没有及时清理,那么最糟糕的情况就是有人无法使用那个资源。当被锁定对象的状态不一致时清理的锁是正确性问题,而不是礼貌问题。
一个通用的、安全的Enter/Leave
模式除了简单地释放不安全的资源之外还有应用程序。我已经看到using
用于自定义锁定、数据库事务等的语句......自定义锁定将从这样的功能中受益匪浅,最终使其既安全又方便。我个人认为,当前版本的lock
语句首先不应该存在,而是使用这样一个更通用的特性来表达。但现在改变这一点为时已晚。【参考方案3】:
Monitor.Enter
至少可以抛出以下异常
null
如果执行Enter
的线程调用了Interrupt
方法,则发生ThreadInterruptedException。
【讨论】:
【参考方案4】:如果它获得了锁,那么没有。
但Monitor.Enter
和try
块之间可能会引发异常。
推荐的方法是新的Enter 方法,是.NET 4 中的新方法:
public static void Enter( obj, ref bool lockTaken )
【讨论】:
它仍然会抛出ArgumentNullException
。
@BoltClock 如果由于某种原因未能获得锁,lockTaken
将为 false。以上是关于Monitor.Enter 可以抛出异常吗?的主要内容,如果未能解决你的问题,请参考以下文章