单用std::memory_order_acquire无效,因其必须与同一原子变量上的std::memory_order_release配对才能建立synchronizes-with关系;否则写线程的非原子写入(如data=42)可能被重排到store之后,导致读线程即使看到标志为真,仍读到未初始化或旧值。

仅用 std::memory_order_acquire 无法单独解决可见性问题——它必须和 std::memory_order_release 配对作用于同一个原子变量,才能让非原子变量的写入对另一线程“可观察”。
为什么单用 acquire 没用?
常见错误是只在读线程加 load(std::memory_order_acquire),却在写线程用 store(true, std::memory_order_relaxed) 或默认 seq_cst。这会导致:
- 写线程中
data = 42可能被重排到ready.store()之后(relaxed 下完全允许) - 即使读线程成功读到
true,data仍可能为未初始化值或旧值 - acquire 本身不“拉取”数据,它只约束自己之后的指令不能上移;若前面没 release 锁定写顺序,它就无从同步
acquire-release 必须成对出现在同一原子变量上
这是建立 synchronizes-with 关系的硬性前提,不是风格建议:
- 写线程:对
std::atomic<bool> ready</bool>调用store(true, std::memory_order_release) - 读线程:对同一个
ready调用load(std::memory_order_acquire) - 不能换变量:用
flag.load(acquire)去等data.store(release)—— 同步断裂 - 不能颠倒顺序:读线程先 load、写线程后 store,或中间穿插其他 relaxed 操作,都会破坏依赖链
acquire 怎么影响非原子变量的读取?
关键不在“看到新值”,而在“看到 release 之前所有写”:
立即学习“C++免费学习笔记(深入)”;
- 写线程中
data = 42和ready.store(..., release)之间不能重排 →data写入一定发生在信号发出前 - 读线程中
ready.load(..., acquire)成功后,printf("%d", data)不会被重排到 load 前 → 它一定读取 release 之前写入的data - 注意:acquire 不保证立即看到
ready的新值(仍依赖 cache coherency),但一旦看到,就一定能看到配套的data
x86 和 ARM 上的差异容易被忽略
acquire/release 的实现成本因架构而异,但语义一致:
- x86:通常不生成额外 CPU 指令,仅插入编译器 barrier(
mov+ 编译器禁止重排) - ARM/AArch64:必须生成
ldar(acquire load)和stlr(release store)等带语义指令 - 误以为“x86 上随便写都行”,迁移到 ARM 后出现偶发错乱——根源常是漏了 release 或用了不同变量


















