std::atomic::exchange 不会阻塞线程,前提是目标类型 lock-free;否则可能退化为加锁实现而产生阻塞风险。它无条件替换并返回旧值,语义区别于 compare_exchange_weak,内存序可依场景降级,且仅支持 trivially copyable 类型。

std::atomic::exchange 会阻塞线程吗
std::atomic::exchange 是一个无锁(lock-free)操作,只要底层硬件支持(比如 x86 的 XCHG 或 LOCK XCHG),它就不会用互斥量或系统调用,也就不会导致线程阻塞或上下文切换。你可以把它理解为一条原子汇编指令的 C++ 封装。
但要注意:如果目标 std::atomic 类型在当前平台不是 lock-free(可通过 is_lock_free() 检查),那 exchange 可能退化为内部加锁实现——此时虽仍保证原子性,但有潜在阻塞风险。
- 用
static_assert(std::atomic<int>::is_always_lock_free, "")</int>在编译期确认常见类型是否 lock-free - 对自定义结构体,
exchange很可能不是 lock-free,慎用 - 不要假设
exchange性能恒定;在 ARM 或某些嵌入式平台上,即使int也可能 fallback 到锁实现
exchange 和 compare_exchange_weak 的行为差异
exchange 总是成功替换并返回旧值,而 compare_exchange_weak 是“条件写”:仅当当前值等于预期值时才写入,否则只读取当前值并返回 false。两者语义完全不同,不能互相替代。
典型误用场景:想实现“若当前为 0 则设为 1”,却用了 exchange(1)——这会无条件覆盖,破坏逻辑。
-
exchange适合“我必须拿到旧值,同时把新值塞进去”,比如资源所有权移交、标志位翻转、队列头节点摘除 -
compare_exchange_weak适合“我只在满足条件时才改,否则重试”,比如无锁栈 push/pop、引用计数递增 -
exchange不会因内存竞争失败重试,也不需要循环;compare_exchange_weak必须配合 do-while 循环使用
exchange 的内存序参数怎么选
exchange 默认使用 std::memory_order_seq_cst,这是最严格也最安全的顺序,但可能带来性能开销。实际中常可降级:
- 纯标志位切换(如
std::atomic<bool> ready</bool>),用memory_order_acq_rel足够:既保证该操作前后的读写不被重排,又避免全局顺序同步 - 仅用于发布/消费场景(如生产者写完数据后置 flag,消费者检查 flag 后读数据),可用
memory_order_release(写端) +memory_order_acquire(读端) - 绝对不要对同一变量混用不同内存序——比如一边用
relaxed,另一边用seq_cst,会导致同步失效
示例:flag.exchange(true, std::memory_order_acq_rel) 比默认更轻量,且仍能正确建立 happens-before 关系。
exchange 对非平凡类型的限制
std::atomic 只允许封装 trivially copyable 类型。这意味着你不能对 std::string、std::vector 或含虚函数/非平凡构造函数的类直接使用 exchange。
常见错误:试图 std::atomic<:shared_ptr>>::exchange</:shared_ptr> ——虽然 std::shared_ptr 是 trivially copyable,但它的原子操作需通过特化版本(如 std::atomic_load/std::atomic_store)完成;直接调用 exchange 可能编译失败或行为未定义。
- 内置类型(
int、void*)、POD 结构体可安全使用exchange - 指针类型(
T*)常用作无锁链表节点交换,exchange是标准做法 - 若需原子更新复杂对象,应封装为指针(
std::atomic<t></t>),而非尝试原子化对象本身
真正容易被忽略的是:即使类型合法,exchange 也只是原子地拷贝其位表示(bitwise copy)。如果对象内含指针或句柄,且外部存在生命周期依赖,原子性本身并不能防止 use-after-free ——这得靠上层逻辑保障。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











