std::memory_order_relaxed不能用于线程间通信,因为它仅保证原子性,不约束内存重排、不提供顺序和可见性保证,导致读到过期值或r1==0&&r2==0等违反直觉的现象。

用 std::memory_order_relaxed 做同步,程序大概率会出错——它只保原子性,不保顺序、不保可见性,不能当锁用。
为什么 memory_order_relaxed 不能用于线程间通信
它只告诉编译器和 CPU:“这个操作本身不可拆分”,但不约束它前后其他内存操作的重排。两个线程靠它传递状态,极易读到过期值或乱序执行。
- 典型现象:
r1 == 0 && r2 == 0同时成立(而直觉上至少一个该是 1) - 场景:一个线程写数据后设标志位,另一个线程轮询标志再读数据
- 问题根源:写数据和设标志可能被重排;轮询和读数据也可能被重排
- 修复关键:写端用
store(..., memory_order_release),读端用load(..., memory_order_acquire)
compare_exchange_weak 忘记检查返回值就崩溃
这个函数不是“一定成功”,而是“尝试原子更新”:成功返回 true,失败返回 false 并把当前值写回期望变量。不检查就继续用旧值,逻辑直接错乱。
- 常见错误:把
compare_exchange_weak当成普通赋值写,漏掉 while 循环或 if 判断 - 后果:可能跳过更新、重复处理、甚至解引用空指针(比如在无锁栈 pop 中)
- 正确模式:必须配合循环重试,或明确处理失败分支
- 示例:
while (!counter.compare_exchange_weak(expected, expected + 1)) {}
混用 memory_order_seq_cst 和 memory_order_acquire/release 破坏 synchronizes-with 关系
只有配对的 release(写)和 acquire(读)才能建立跨线程的 happens-before 关系。如果一端用了 seq_cst,另一端用 relaxed,那这层保证就断了。
- 影响:看似用了原子操作,但修改的数据对另一线程仍不可见
- 调试难点:问题只在特定 CPU 架构(如 ARM/PowerPC)上暴露,x86 因强内存模型常“碰巧”正常
- 建议:除非明确需要全局顺序(比如实现互斥锁),否则避免默认用
seq_cst;通信路径两端内存序要语义匹配 - 注意:
seq_cstload 可以和releasestore 同步,但relaxedload 不行
最易被忽略的是:原子操作本身不会自动“传播”非原子变量的修改——哪怕它们在同一个 struct 里。想让其他线程看到你改过的 data,光原子地改 flag 不够,必须用 release/acquire 把它们绑在一起。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











