ECMA-335规范说明如下:
*获取锁(System.Threading.Monitor.Enter或输入同步方法)应隐式执行易失性读操作,释放锁(System.Threading.Monitor.Exit或保留同步方法)应隐式执行易失性写操作操作.(......)
易失性读取具有获取语义,这意味着保证在CIL指令序列中的读取指令之后发生的对存储器的任何引用之前发生读取.易失性写入具有释放语义,这意味着写入保证在CIL指令序列中的写入指令之前的任何存储器引用之后发生.*
这意味着编译器不能将语句移出Monitor.Enter/Monitor.Exit块,但不禁止其他语句移入块中.也许,甚至可以将另一个Monitor.Enter移动到块中(因为可以交换易失性写入,然后是易失性读取).那么,可以使用以下代码:
class SomeClass
{
object _locker1 = new object();
object _locker2 = new object();
public void A()
{
Monitor.Enter(_locker1);
//Do something
Monitor.Exit(_locker1);
Monitor.Enter(_locker2);
//Do something
Monitor.Exit(_locker2);
}
public void B()
{
Monitor.Enter(_locker2);
//Do something
Monitor.Exit(_locker2);
Monitor.Enter(_locker1);
//Do something
Monitor.Exit(_locker1);
}
}
Run Code Online (Sandbox Code Playgroud)
,变成以下的等价物:
class SomeClass
{
object _locker1 = new object();
object _locker2 = new object();
public void A()
{
Monitor.Enter(_locker1);
//Do something
Monitor.Enter(_locker2);
Monitor.Exit(_locker1);
//Do something
Monitor.Exit(_locker2);
}
public void B()
{
Monitor.Enter(_locker2);
//Do something
Monitor.Enter(_locker1);
Monitor.Exit(_locker2);
//Do something
Monitor.Exit(_locker1);
}
}
Run Code Online (Sandbox Code Playgroud)
,可能导致死锁?或者我错过了什么?
ECMA-335 规范比CLR(以及所有其他实现)使用的规范弱很多。
我记得读过(此处)有关 Microsoft 首次尝试使用较弱的内存模型移植到 IA-64 的内容。他们有太多自己的代码依赖于双重检查锁定惯用法(在较弱的内存模型下会被破坏),因此他们只是在该平台上实现了更强的模型。
Joe Duffy 有一篇很棒的文章,为我们凡人总结了(实际的)CLR 内存模型。还有一个 MSDN 文章的链接,该文章更详细地解释了 CLR 与 ECMA-335 的不同之处。
我不认为这在实践中是一个问题;假设 CLR 内存模型,因为其他人都这样做。此时没有人会创建一个弱实现,因为大多数代码都会崩溃。