4

在.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 中)以确保释放监视器。

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

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

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

在Visual Studio 2012 的线程同步(C# 和 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)?到底是怎么做的有什么区别吗?

4

1 回答 1

6

来自Eric Lippert 的博客

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

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

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

在 C# 4.0 中,我们更改了 lock 以便它现在生成代码,就好像它是

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

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

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

可悲的是没有。[...]

另一方面,C# 4.0 语言规范说:

形式的锁定语句

lock (x) ...

其中 x 是引用类型的表达式,精确地等价于

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

除了 x 只被评估一次。

于 2013-02-22T12:15:03.987 回答