【问题标题】:atomic operations and visibility in C++ [duplicate]C ++中的原子操作和可见性[重复]
【发布时间】:2021-05-13 09:08:30
【问题描述】:

考虑下面的代码:

static std::atomic<int> x;

// Thread 1
x.store(7, std::memory_order_relaxed);

// Thread 2
x.load(std::memory_order_relaxed);

进一步假设 线程 2 执行了 load 几个周期 em> 线程 1 执行了store

是否保证 Thread 2 会在所有硬件平台上读取值 7?换句话说,在线程 1 执行 store 之后,是否保证值 7 对稍后执行相同变量的轻松加载的线程立即可见?

【问题讨论】:

  • 你为什么老是问同样的问题?

标签: c++ atomic stdatomic


【解决方案1】:

是的,它将是可见的。 关于宽松的记忆顺序以及与更强的记忆顺序相比它们的作用,这已成为一个普遍的问题。

一个比放松的记忆顺序更强的记忆顺序会做以下两件事:

  • 同步非原子数据
  • 防止在同一线程中重新排序指令

不同的顺序保证不同​​的同步策略(只读,只写,读和写)和不同的重排序预防(只在指令之前执行的,只在指令之后执行的或两者兼而有之)。内存顺序放宽不保证任何这些,它只保证原子变量在线程之间是可见的。

在您的示例中,除了一个线程中的存储和另一个线程中的加载之外,没有其他指令。另外,没有可跨线程同步的非原子内存,唯一的内存已经是原子的。

所以没有必要使用比memory_order_relaxed 更重的东西。上面的 sn-p 是有效的,会产生想要的结果。

【讨论】:

  • “是”这个答案太简单了,IMO。 OP 谈论的是实际时间/周期,而不是具有“同步”/“发生在之前”关系的线程。他们说线程 2 在线程 1 执行存储之后几个周期执行了加载。缓存是连贯的,因此它最终会在没有任何显式刷新的情况下变得可见,但在 CPU 架构术语中“执行”存储只是意味着将其放在私有存储缓冲区中,最终在执行后的某个时间提交(并变得可见)是已知是非投机性的。这是商店 OoO 执行官的关键。
  • 有关存储缓冲区和执行的更多详细信息,请参阅Can a speculatively executed CPU branch contain opcodes that access RAM?。但无论如何,这里的关键是其他内核可以一直加载旧值,直到它们从内核接收到 RFO(读取所有权),试图获取缓存行的所有权,以便它可以提交其存储。 (存储缓冲区会延迟存储可见性。如果线程 1 还进行了一些后续加载,即使在保持其他所有内容有序的强排序 CPU 上,这也是 StoreLoad 重新排序的方式。)
  • 我在链接副本 (memory_order_relaxed and visibility) 上发布了答案。
  • 我一般不同意你的观点。不是因为他们不正确,因为他们不相关。我们唯一关心的是 C++ 标准。 C++ 标准规定,如果存储实际发生在加载之前并且存储+加载是原子的,则存储对加载是可见的。这里就是这种情况。没有什么“过于简单化”,如果有的话 - 欢迎您提出更详细的 C++ 标准。您不必了解 CPU 机制就可以在 C++ 中编写良好/安全的并发代码。您所需要的一切已经在标准中了。
  • 那么对 OP 所要求的正确答案是“C++ 除了线程在内存中观察到的内容外,没有任何‘同一时间’的概念”。也就是说,这不是 C++ 可以回答的问题。您完全正确,您不必了解 CPU 机器就可以在 C++ 中编写良好/安全的代码,您的答案应该这样说,并告诉 OP 他们提出了错误的问题。不要只回答不同的问题而不提!
猜你喜欢
  • 1970-01-01
  • 2021-09-20
  • 2013-05-07
  • 1970-01-01
  • 1970-01-01
  • 2014-11-30
  • 2017-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多