仅用 std::memory_order_release 无法保证可见性,必须与同一原子变量上的 std::memory_order_acquire 配对使用;否则 data 写入可能被重排到 store 之后,导致读线程看到 ready==true 但 data 未更新。

单用 std::memory_order_release 不能解决可见性问题——它必须和 std::memory_order_acquire 配对作用于同一个 std::atomic 变量,才能让非原子写入对另一线程“可观察”。
为什么只在写线程用 memory_order_release 会失效
常见错误是:写线程用了 ready.store(true, std::memory_order_release),但读线程却用 ready.load(std::memory_order_relaxed) 或默认 seq_cst。这会导致:
-
data = 42被编译器或 CPU 重排到ready.store()之后(relaxed下完全允许) - 即使读线程看到
ready == true,data仍可能是未初始化值、旧值,甚至触发未定义行为 -
memory_order_release本身不“广播”数据,它只约束自己前面的写不能后移;没有配套的acquire,这个约束就断在半路
memory_order_release 必须和 memory_order_acquire 配对在同一变量上
同步关系不是靠“用了 release 就自动生效”,而是靠两个操作在同一个原子变量上的顺序依赖。例如:
std::atomic<bool> ready{false};
int data = 0;
// 线程 A(生产者)
data = 42; // 普通写
ready.store(true, std::memory_order_release); // release:锁住前面所有写
// 线程 B(消费者)
while (!ready.load(std::memory_order_acquire)) { } // acquire:解锁后面所有读
assert(data == 42); // ✅ 一定成立
</bool>
关键点:
- 不能换变量:用
flag.load(acquire)去等data.store(release)—— 同步断裂 - 不能颠倒顺序:读线程先
load、写线程后store,或中间穿插relaxed操作,都会破坏依赖链 - 必须是同一变量:两个操作都作用于
ready,否则无synchronizes-with关系
x86 和 ARM 上的实现差异容易被忽略
memory_order_release 的语义一致,但底层指令因架构而异:
- x86:通常不生成额外 CPU 指令,仅插入编译器 barrier(如
mov+ 编译器禁止重排) - ARM/AArch64:必须生成
stlr(store-release)指令,否则无法保证同步语义 - 误以为“x86 上随便写都行”,迁移到 ARM 后出现偶发错乱——根源常是漏了
release,或用了不同变量
真正危险的不是写错一次,而是写对了但没配对、配对了但跨了变量、跨了变量还跑在 x86 上侥幸通过测试——等上 ARM 或高负载环境才暴露。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











