【问题标题】:Lock keyword calls Monitor.Enter(Object) or Enter(Object, Boolean)?Lock 关键字调用 Monitor.Enter(Object) 或 Enter(Object, Boolean)?
【发布时间】:2013-02-08 01:15:04
【问题描述】:

在.NET Framework 4.5的Monitor文档中找到一句话,说lock关键字使用了Monitor的Enter(Object, Boolean)方法:

Enter 和 Exit 方法提供的功能与 C# lock 语句(Visual Basic 中的 SyncLock)提供的功能相同,只是 lock 和 SyncLock 将 Enter(Object, Boolean) 方法重载和 Exit 方法包装在一个try...finally 块(在 Visual Basic 中 Try...Finally)以确保释放监视器。

另一方面,在Monitors 的概览中有:

Visual Basic SyncLock 和 C# lock 语句使用 MonitorEnter 来获取锁,并使用 MonitorExit 来释放它。

上面的MonitorEnter指的是不同于之前版本的Enter方法,即:Enter(Object)

在 Visual Studio 2012 的 Thread Synchronization (C# and Visual Basic) 中,有一个示例如何锁定监视器:

System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}

还有Enter(Object)版本。

什么是真的? lock 语句是否调用Enter(Object, Boolean)Enter(Object)? 到底怎么做有什么区别?

【问题讨论】:

  • 他们都是真的。取决于您所针对的 .NET 版本。
  • @HansPassant:我只从 .NET 4.5 和 Visual Studio 2012 获得这些报价

标签: c# .net locking monitor


【解决方案1】:

来自Eric Lippert’s Blog

回想一下,lock(obj){body} 是 [在 C# 3.0 及更早版本中] 的语法糖

var temp = obj;
Monitor.Enter(temp);
try { body }
finally { Monitor.Exit(temp); }

这里的问题是,如果编译器在监视器进入和 try-protected 区域之间生成一个无操作指令,那么运行时可能会在监视器进入之后但在尝试之前抛出线程中止异常。在那种情况下, finally 永远不会运行,所以锁泄漏,可能最终导致程序死锁。如果这在未优化和优化的构建中是不可能的,那就太好了。

在 C# 4.0 中,我们更改了锁,因此它现在生成的代码就像它一样

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

问题现在变成了别人的问题; Monitor.Enter 的实现负责以一种不受线程中止异常影响的方式自动设置标志。

所以现在一切都很好,对吧?

很遗憾,没有。 [...]

另一方面,C# 4.0 Language Specification 说:

形式的锁语句

lock (x) ...

其中 x 是引用类型的表达式,完全等价于

System.Threading.Monitor.Enter(x);
try {
   ...
}
finally {
   System.Threading.Monitor.Exit(x);
}

除了 x 只计算一次。

【讨论】:

  • 文档是指 4.5 和 Visual Studio 2012。他们只是复制粘贴过时的旧文本吗?
  • 可能微软实现了 Enter(Object, Boolean) ,因此它破坏了一半的文档和标准。其他人可能会执行标准。
  • 因为 Eric Lippert 是 part of the C# design team,他很有可能知道自己在说什么,而且他比文档更正确。
  • @yBee:我们深思熟虑地决定不要让文档过于复杂; Enter 是否在受保护区域内对于用户来说绝大多数情况下是不可见的。如果目标 CLR 的版本具有公共 Enter(obj, bool) 方法,则 C# 编译器现在在 try 中生成 Enter,如果没有,则回退到 Enter(obj);许多版本的 CLR 没有可访问的 Enter(obj, bool) 方法。解释规范中的所有内容只是不必要的混乱;您真正需要知道的是锁定与Entering 相同。
猜你喜欢
  • 1970-01-01
  • 2021-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
相关资源
最近更新 更多