我不确定我是否完全理解(并且我可能全错)C++11 中原子性和内存排序的概念。 让我们以这个简单的单线程示例为例:
int main()
{
std::atomic<int> a(0);
std::atomic<int> b(0);
a.store(16);
b.store(10);
return 0;
}
在这个单线程代码中,如果 a 和 b 不是原子类型,编译器可能会以在汇编代码中的方式重新排序指令,例如,我有一条移动指令,在 a 之前将 10 分配给“b”将指令移动到指定的 16 到“a”。 因此,对我来说,作为原子变量,它保证我在“b 移动指令”之前有“a 移动指令”,正如我在源代码中所述。 之后是处理器及其执行单元、预取指令和乱序盒。并且该处理器可以在“a 指令”之前处理“b 指令”,无论汇编代码中的指令顺序如何。 因此,在将 16 个存储在寄存器/存储缓冲区或高速缓存中之前,我可以将 10 个存储在寄存器或处理器的存储缓冲区或高速缓存中。
根据我的理解,这就是内存排序模型的诞生。从那一刻起,如果我让默认模型顺序一致。有人向我保证,在主内存中清除这些值(10 和 16)将遵循我在源代码中存储的顺序。这样处理器将开始刷新寄存器或缓存,其中 16 存储到主内存中用于更新“a”,然后它将刷新主内存中的 10 用于“b”。
所以这种行为确实让我明白,如果我使用宽松的内存模型。只有最后一部分不保证,因此主内存中的刷新可能会完全困惑。
抱歉,如果您阅读我的内容遇到麻烦,我的英语仍然很差。但谢谢你们抽出时间。
最佳答案
C++ 内存模型是关于抽象机器和值可见性,而不是关于“主内存”、“写入队列”或“刷新”等具体事物。
在您的示例中,内存模型指出,由于对 a
的写入发生在对 b
的写入之前,因此任何从 b 读取 10 的线程
在随后从 a
读取时必须看到 16(当然,除非此后已被覆盖)。
这里重要的是建立发生之前的关系和值(value)可见性。如何映射到缓存和内存取决于编译器。在我看来,最好停留在抽象级别上,而不是尝试将模型映射到您对硬件的理解,因为
- 您对硬件的理解可能是错误的。硬件比 C++ 内存模型还要复杂。
- 即使您现在的理解是正确的,更高版本的硬件也可能有不同的模型,至少在子系统中是这样。
- 通过映射到硬件模型,您可能会对不同硬件模型的影响做出错误的假设。例如。如果您了解内存模型如何映射到 x86 硬件,您将无法理解 PowerPC 上消耗和获取之间的细微差别。
- C++ 模型非常适合推理正确性。
关于c++ - C++11 中的内存排序与主内存刷新排序有关吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29669700/