Lock关键字调用Monitor.Enter(Object)或Enter(Object,Boolean)

本文关键字:Object Enter Boolean 关键字 Lock 调用 Monitor | 更新日期: 2023-09-27 18:00:27

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

另一方面,在监视器的Overview中有:

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

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

在Visual Studio 2012的线程同步(C#和Visual Basic)中,有一个锁包装Monitor的示例:

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

还有Enter(对象)版本。

什么是真的?lock语句调用Enter(Object,Boolean)还是Enter(Object)?实际操作方式是否存在差异?

Lock关键字调用Monitor.Enter(Object)或Enter(Object,Boolean)

来自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中,我们更改了锁,这样它现在就可以像一样生成代码

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只被评估一次之外。