我将回答在真实 CPU 上的实际实现中会发生什么,因为仅基于标准的答案几乎不能说出关于时间或“即时性”的任何有用信息。
MESI 只是 ISO C++ 没有什么可说的实现细节。ISO C++ 提供的保证只涉及顺序,而不涉及实际时间。ISO C++ 故意不特定,以避免假设它将在“普通”CPU 上执行。在需要显式刷新以实现存储可见性的非连贯机器上的实现在理论上可能是可能的(尽管对于发布/获取和 seq-cst 操作的性能可能很糟糕)
C++ 在时序方面不够具体,甚至允许在单核协作多任务系统(无抢占)上实现,编译器偶尔会插入自愿让步。(没有任何易失性访问或 I/O 的无限循环是 UB)。 假设您认为调度程序时间片仍然是“合理”的时间量,那么在一次实际上只能执行一个线程的系统上的 C++ 是完全可以和可能的。(如果您让步或以其他方式阻止,则更少。)
甚至 ISO C++ 用来提供关于排序的保证的形式主义模型也与硬件 ISA 定义其内存模型的方式大不相同。C++ 正式保证纯粹是在发生之前和同步方面,而不是“重新”排序石蕊测试或任何类似的东西。例如,如何在 C++11 中实现 StoreLoad 屏障?无法回答纯 ISO C++ 形式主义。它可以显示 C++ 的保证是多么的薄弱;例如,根据 C++ 形式,所有 seq_cst 操作的总顺序的存在不足以暗示基于它的发生之前。但在现实生活中,对于具有一致缓存和仅本地(在每个 CPU 内核内)内存重新排序的系统来说就足够了。
当线程 A 将值存储到std::atomic
这取决于您所说的“做”商店是什么意思。
如果您的意思是从存储缓冲区提交到 L1d 缓存,那么是的,那是存储变得全局可见的时刻,在使用 MESI 为所有 CPU 内核提供一致的内存视图的普通机器上。
尽管请注意,在某些 ISA 上,允许其他一些线程在通过 cache全局可见之前查看存储。(即硬件内存模型可能不是“多副本原子”,并允许 IRIW 重新排序。POWER 是我所知道的在现实生活中这样做的唯一示例。请参阅是否总是可以看到两个原子写入到不同线程中的不同位置其他线程的顺序相同吗?有关硬件机制的详细信息:SMT 线程之间的已退休 akagraded stores 的存储转发。)
如果您的意思是在本地执行以便稍后在该线程中加载可以看到它,那么不会。 std::atomic 可以使用弱于 seq_cst 的 memory_order。
所有主流 ISA 都有足够弱的内存排序规则,以允许存储缓冲区将指令执行从提交到缓存解耦。在我们确定它们是否在正确的执行路径上之前,这也允许通过在执行后将存储放在某个私有的地方来进行推测性的乱序执行。(在存储指令从后端的乱序部分退出之前,存储不能提交到 L1d,因此已知是非推测性的。)
如果您想等待其他线程可以看到您的商店,然后再进行任何后续加载,请使用atomic_thread_fence(memory_order_seq_cst);
. (在标准选择 C++ 的“普通”ISA 上 -> asm 映射将编译为完全障碍)。
在大多数ISA 上,seq_cst 存储(默认)也将停止该线程中的所有后续加载(和存储),直到该存储全局可见。但是在 AArch64 上,STLR 是一个顺序释放存储,以后加载/存储的执行不必停止,除非/直到 LDAR(获取加载)即将执行,而 STLR 仍在存储缓冲区中。这实现了尽可能弱的 SC 语义,假设 AArch64 硬件实际上以这种方式工作,而不是仅仅将其视为存储 + 全屏障。
但请注意,只需要阻止以后的加载/存储;寄存器上的 ALU 指令的乱序执行仍然可以继续。但是,例如,如果您期望由于 FP 操作的依赖链而产生某种时间效应,那么在 C++ 中您就无法依赖它。
即使您确实使用了 seq_cst ,所以在其他人可以看到存储之前,该线程中没有任何事情发生,这仍然不是即时的。例如,在主流现代 Intel x86上,真实硬件上的内核间延迟可能约为40ns 。(这个线程不必在内存屏障指令上停顿那么久;其中一些时间是另一个线程上的缓存未命中,试图读取被该内核的 RFO 无效的行以获得独占所有权。)或者当然对于共享物理内核的 L1d 缓存的逻辑内核来说要便宜得多:生产者-消费者在超同级与非超同级之间共享内存位置的延迟和吞吐量成本是多少?