Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/264.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 联锁监视器。进入和监视。退出块_C#_Multithreading - Fatal编程技术网

C# 联锁监视器。进入和监视。退出块

C# 联锁监视器。进入和监视。退出块,c#,multithreading,C#,Multithreading,ECMA-335规范规定了以下内容: *获取锁(System.Threading.Monitor.Enter或进入同步方法)应隐式执行易失性读取操作,释放锁(System.Threading.Monitor.Exit或离开同步方法)应隐式执行易失性写入操作。 (……) volatile read具有acquire语义,这意味着在CIL指令序列中的read指令之后发生的对内存的任何引用之前,保证读取发生。易失性写入具有释放语义,这意味着写入保证在CIL指令序列中写入指令之前的任何内存引用之后发生*

ECMA-335规范规定了以下内容:

*获取锁(System.Threading.Monitor.Enter或进入同步方法)应隐式执行易失性读取操作,释放锁(System.Threading.Monitor.Exit或离开同步方法)应隐式执行易失性写入操作。 (……)

volatile read具有acquire语义,这意味着在CIL指令序列中的read指令之后发生的对内存的任何引用之前,保证读取发生。易失性写入具有释放语义,这意味着写入保证在CIL指令序列中写入指令之前的任何内存引用之后发生*

这意味着编译器不能将语句移出Monitor.Enter/Monitor.Exit块,但不禁止将其他语句移到块中。也许,甚至可以将另一个Monitor.Enter移动到块中(因为可以交换volatile write和volatile read)。 因此,请输入以下代码:

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);
    }
}

,可能导致死锁?还是我遗漏了什么?

当你使用
lock
Monitor时。进入
Monitor.Exit
这是完全隔离,这意味着它将在内存
线程中创建一个“屏障”。MemoryBarrier()
在begining或锁“
Monitor.Enter
”处,在锁结束之前“
Monitor.Exit
“.So没有操作会在锁前后移动,但请注意,锁本身内的操作可以从其他线程角度交换,但这从来不是问题,因为锁将保证互斥,因此只有一个线程会同时执行锁内的代码。无论如何,重新排序不会发生在单个线程中,也就是说,当多线程进入相同的代码区域时,它们可能会看到顺序不同的指令

我强烈建议您在文章中阅读更多关于
MemoryBarrier
和全围栏和半围栏的内容

Edit:注意,这里我描述的是
lock
是完全隔离的事实,但不是你所知道的“死锁”,你描述的场景永远不会发生,因为像@Hans提到的那样,方法调用永远不会发生重新排序,即:

Method1();
Method2();
Method3();

将始终按顺序执行,但其中的指令可能会重新排序,就像多线程执行
Method1()
中的代码一样。

ECMA-335规范比其他规范弱得多

我记得读过(这里说)微软第一次尝试使用较弱的内存模型移植到IA-64。他们有太多自己的代码依赖于双重检查锁定习惯用法(在较弱的内存模型下),以至于他们只是在该平台上实现了较强的模型

乔·达菲为我们这些凡人总结了(实际的)CLR记忆模型。还有一个指向MSDN文章的链接,该文章更详细地解释了CLR与ECMA-335的区别


我认为这在实践中不是一个问题;假设CLR内存模型,因为其他人都是这样做的。在这一点上,没有人会创建一个弱的实现,因为大多数代码都会简单地中断。

是的,许多源都声明Monitor.Enter和Monitor.Exit是完全内存隔离,但ECMA-335规范本身确保在开始和结束时只有一半的隔离。然而,您可能仍然对Microsoft的实施是正确的,因为它比ECMA-335更严格。我承认这个问题更多的是关于理论而不是实践,但是另一个CLI实现可能不那么严格,所以这个问题仍然是开放的。相关的是编译器和抖动都不会对方法调用重新排序。嗯……有趣的问题。不过,我不确定我是否已经准备好购买方法重新排序参数。我的意思是,如果内联优化发生在提升优化之前。规范中有没有条款可以防止这种情况的发生?事实上,如果这种情况在任何地方都不起作用,那将产生灾难性的影响。