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