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);
}
},变成相当于以下内容的东西:
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);
}
}可能会导致死锁?还是我漏掉了什么?
发布于 2011-08-01 18:49:09
ECMA-335规范比CLR (和所有其他实现)使用什么?弱得多.
我记得我读过(异端邪说)微软第一次尝试使用较弱的内存模型移植到IA-64。他们有太多自己的代码依赖于双重检查的锁定成语(在较弱的内存模型下是断了 ),所以他们只是在这个平台上实现了更强的模型。
Joe已经为我们这些普通人总结了(实际的) CLR内存模型。还有一个链接到MSDN文章,更详细地解释了CLR与ECMA-335的不同之处。
我不认为这在实践中是一个问题;只要假设CLR内存模型,因为其他人都这样做。此时没有人会创建一个弱实现,因为大多数代码都会崩溃。
发布于 2011-08-01 18:37:07
当您使用lock或Monitor.Enter和Monitor.Exit时,这是满栅栏,这意味着它将在开始时或锁"Monitor.Enter“和锁"Monitor.Exit”结束之前在内存Thread.MemoryBarrier()中创建一个“屏障”。因此,没有操作将在锁之前和之后移动,但是请注意,锁本身内的操作可以从其他线程透视图中交换,但这从来不是一个问题,因为锁将保证互斥,因此只有一个线程将同时在锁内执行代码。无论如何,重新排序不会在单个线程中发生,也就是说,当多线程输入相同的代码区域时,它们可能会看到不同顺序的指令。
我强烈建议您在这文章中阅读更多关于这和完整和半栅栏的内容。
编辑:注意到,这里我描述的事实是,lock是完全隔离的,但没有提到您所知道的“死锁”,您所描述的场景永远不会发生,因为就像@提到的那样,方法调用的重新排序永远不会发生,即:
Method1();
Method2();
Method3();将始终按顺序执行,但其中的指令可能会重新排序,就像多线程执行Method1()中的代码时一样。
https://stackoverflow.com/questions/6902612
复制相似问题