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

Posted

技术标签:

【中文标题】在没有易失性机制的情况下,CPU 何时写入主存?【英文标题】:When does a CPU write to main memory in the absence of a volatile mechanism? 【发布时间】:2020-07-28 11:46:56 【问题描述】:

考虑在 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) 只有一个线程可以访问 Data 和 b) Data 中的字段永远不会被“撕裂”。 Lock 提供了一个内存屏障,据我所知,这在这两种情况下不会有任何明显的影响。 由于字段未标记volatile,并且由于没有Volatile.Read()Volatile.Write() 操作,这三个字段将被写入缓存,而不是直接写入主内存。 直接写入主内存的唯一方法是通过上述“易失性”机制之一,因为这些机制使用ref 操作并禁用优化,从而导致主内存读/写。 查看代码,CPU 会在我不知道的某个时间点将这些字段写入主内存。 我不明白为什么多个线程可以保证看到这三个字段的最新版本,尤其是在 ARM 等弱排序内存架构上。

我的问题是:我如何确定在调用Set() 之后调用ReadCopy() 会看到三个字段的最新值?调用线程可能位于不同的内核上,并且有自己的 Data 缓存副本。

“易变”机制的存在显然是有原因的。该示例通常围绕访问非锁定内存段展开。但是,这里的例子呢?我从未见过使用lock 并且使用易失机制的代码。

【问题讨论】:

您说,“2.Lock 提供了内存屏障...”为什么您认为内存屏障不足以保证ReadCopy() 将返回大多数提供的值最近的Set(...) 电话? (我不是 C# 程序员,但锁/互斥体/无论你怎么称呼它们都可以在我使用过的每一种 其他 编程语言中提供这种保证。) afana.me/archive/2015/07/10/memory-barriers-in-dot-net.aspx 我不知道内存屏障会将所有未完成的缓存行写入主内存这一事实。 AFAIK,内存屏障与防止重新排序有关,而不是写入主内存。 Java 语言承诺这一点:线程 A 在释放某个锁 L 之前所做的任何分配对于线程 B 线程 B 之后都是可见的锁定同一个锁 L。这是一个内存屏障。这限制了硬件对线程 A 和 B 进行的读取和存储重新排序的能力。请注意,没有提到“缓存”和“主内存”。这些词不会出现在 Java 语言的正式规范中任何地方。就像我说的,我不懂 C#,但 C# 是受 Java 启发的。我敢打赌,您无需考虑“缓存”或“主内存”就可以理解 C# 的工作原理。 【参考方案1】:

引自 Igor Ostrovsky 的文章 C# - The C# Memory Model in Theory and Practice:

当一个锁定的代码块执行时,可以保证看到所有来自该块之前的块的写入,按锁定的顺序。此外,保证不会看到任何来自按照锁的顺序跟随它的块的写入。

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

我认为这很彻底地回答了你的问题!

还有第二部分:C# - The C# Memory Model in Theory and Practice, Part 2

【讨论】:

以上是关于在没有易失性机制的情况下,CPU 何时写入主存?的主要内容,如果未能解决你的问题,请参考以下文章

编译器可以通过指向易失性的指针优化存储吗? [复制]

如何编写Linux下Nand Flash驱动

非易失性MRAM数据写入与读取

智能卡:非易失性存储器的状态已更改 - 0x6581

将易失性数组转换为非易失性数组

Everspin授权代理非易失性串口mram存储器--MR25H256CDF