std::atomic::exchange 是原子rmw操作,而 std::exchange 对 atomic 类型是非原子的 load+store;前者返回旧值并保证原子性,后者仅调用 operator= 和 load(),行为完全不同。

std::atomic::exchange 是无条件原子交换,不是锁,但能用来实现锁逻辑;它不阻塞线程,也不需要互斥量,但必须配合正确的内存序和使用模式。
std::atomic::exchange 为什么不能当 std::exchange 用?
很多人一看到名字里有 “exchange”,就直接写 std::exchange(atomic_var, new_val),结果代码在多线程下出错。这不是函数用错了,是根本没调对函数:
-
std::exchange是<utility></utility>里的泛型函数,对std::atomic类型只是调用其operator=和load()—— 这两步是非原子的,可能被其他线程打断; -
std::atomic::exchange()是std::atomic的成员函数,从 C++11 就存在,才真正执行单条原子 RMW(读-改-写)指令; - 编译器不会报错,但行为完全不同:前者是“读旧值 + 写新值”两步,后者是“原子交换”一步。
典型错误写法:int old = std::exchange(counter, 42); → 实际触发非原子 load + store;正确写法:int old = counter.exchange(42);
什么时候该用 std::atomic::exchange 而不是 compare_exchange?
选 exchange 的核心判断标准是:你是否“不关心当前值,只管换掉它”。
- 适用场景:
std::atomic<bool></bool>做一次性标志置位(如关机请求)、std::atomic<void></void>替换指针、计数器重置为初值; - 不适用场景:实现自旋锁、无锁栈顶更新、引用计数递减——这些都需要“先检查再改”,必须用
compare_exchange_weak或compare_exchange_strong; - 性能差异:x86 上
exchange编译为XCHG指令,零失败、无重试;而compare_exchange在冲突时需循环重试,但更灵活。
比如设置全局终止信号:bool was_running = running_flag.exchange(false); —— 无需知道之前是不是 true,只要确保现在是 false,并拿到上一状态即可。
memory_order 参数怎么选?别乱设 seq_cst
std::atomic::exchange 默认用 memory_order_seq_cst,这是最安全也最重的内存序。但在某些场景可以降级:
- 仅用于单生产者/单消费者标志位(如启动/停止开关),且不依赖其他变量顺序,可用
memory_order_acq_rel; - 若只是纯粹“采样+覆盖”(如日志缓冲区切换),且后续操作不依赖该值的可见性顺序,甚至可尝试
memory_order_relaxed; - 但注意:
memory_order_relaxed下,编译器和 CPU 可能重排该操作前后的访存,容易引入隐晦 bug;ARM/PowerPC 等弱内存模型平台尤其敏感。
建议:首次实现时全用默认 seq_cst;压测确认瓶颈后再针对性优化,且必须配合同步点验证语义不变。
std::atomic::exchange 对指针和智能指针的特殊处理
它支持任意可复制/可移动类型,但对指针类操作要格外注意语义:
- 裸指针交换(如
std::atomic<int></int>):交换的是地址值,不涉及所指对象生命周期,务必确保原指针指向资源仍有效或已被安全释放; -
std::atomic<:shared_ptr>></:shared_ptr>是合法的,但exchange只原子交换控制块指针,不保证T的构造/析构原子性; -
std::unique_ptr不能直接塞进std::atomic(不满足CopyConstructible),得用std::atomic<:shared_ptr></:shared_ptr>或手动封装指针 + 引用计数。
常见误用:std::atomic<:unique_ptr>> ptr;</:unique_ptr> → 编译失败;正确做法是用 std::atomic<int></int> 配合 std::unique_ptr::release() / reset() 手动管理。
真正容易被忽略的点是:exchange 返回的是旧值的副本,不是引用;如果旧值是个大对象(比如含动态分配内存的结构体),频繁调用可能引发意外拷贝开销——这时候得评估是否真需要返回旧值,或者改用 store() + 单独 load() 分离操作。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











