C# 在没有易失性机制的情况下,CPU何时写入主存?

C# 在没有易失性机制的情况下,CPU何时写入主存?,c#,multithreading,caching,concurrency,locking,C#,Multithreading,Caching,Concurrency,Locking,考虑在x64或ARM上运行的多核/多处理器环境中的以下C#代码: public sealed class Trio { public long A; public long B; public long C; } public static class MP { private static readonly object locker = new object(); private static readonly Trio Data = new Tri

考虑在x64或ARM上运行的多核/多处理器环境中的以下C#代码:

public sealed class Trio
{
    public long A;
    public long B;
    public long C;
}

public static class MP
{
    private static readonly object locker = new object();
    private static readonly Trio Data = new Trio();

    public static Trio ReadCopy()
    {
        lock (locker)
        {
            return new Trio { A = Data.A, B = Data.B, C = Data.C };
        }
    }

    public static void Set(long a, long b, long c)
    {
        lock (locker)
        {
            Data.A = a;
            Data.B = b;
            Data.C = c;
        }
    }
}
线程的同步显然得到了清晰的处理

然而,根据我的理解,我有一个基于以下观察的问题:

  • lock
    语句保证a)只有一个线程可以访问
    数据
    ,b)数据中的字段永远不会被“撕裂”
  • Lock
    提供了一个内存屏障,就我所见,它在这两种情况下不会有任何明显的效果
  • 由于字段未标记为
    volatile
    ,并且没有
    volatile.Read()
    volatile.Write()
    操作,因此这三个字段将写入缓存,而不是直接写入主内存
  • 直接写入主存的唯一方法是通过上述“易失性”机制之一,因为这些机制使用
    ref
    操作并禁用优化,从而导致主存读/写
  • 查看代码,CPU会在我不知道的某个点将这些字段写入主内存
  • 我不明白为什么多线程可以保证看到这三个字段的最新版本,特别是在弱有序内存体系结构(如ARM)上
  • 我的问题是:如何确保在调用
    Set()
    之后调用
    ReadCopy()
    会看到这三个字段的最新值?调用线程可以位于不同的核心上,并且有自己的
    数据的缓存副本

    “不稳定”机制的存在显然是有原因的。该示例通常围绕访问未锁定的内存段展开。但是,这里的例子是什么?我从未见过使用
    锁的代码
    和使用易失性机制的代码。

    引用Igor Ostrovsky的文章:

    当一个被锁定的代码块执行时,它保证按照锁的顺序看到来自该块之前的块的所有写操作。此外,它还保证不会看到按照锁的顺序从它后面的块写入的任何内容

    简而言之,锁隐藏了内存模型的所有不可预测性和复杂性:如果正确使用锁,就不必担心内存操作的重新排序

    我认为这完全回答了你的问题


    还有第2部分:

    您说过,“2.
    Lock
    提供了一个内存屏障…”为什么您认为内存屏障不足以保证
    ReadCopy()
    将返回最近的
    集合(…)
    调用提供的任何值?(我不是一个C#程序员,但锁/互斥锁/任何你称之为它们的东西在我使用过的其他编程语言中都提供了这种保证。)我不知道内存屏障会将所有未完成的缓存线写入主内存。另外,内存障碍与防止重新排序有关,而不是写入主内存。Java语言承诺:线程a在释放锁L之前所做的任何分配,在线程B锁定同一个锁L之后,线程B都可以看到。这是内存障碍。这限制了硬件对线程A和线程B进行的回迁和存储进行重新排序的能力。请注意,没有提到“缓存”和“主内存”。这些词在Java语言的正式规范中没有出现。就像我说的,我不知道C,但C的灵感来自Java。我敢打赌,你不需要考虑“缓存”或“主内存”,就能理解C#是如何工作的