std::atomic 是 c++11 提供的原子操作模板类,仅保证单变量读-改-写操作的原子性,不能替代互斥锁实现多字段同步;它支持 load/store/fetch_add 等原子操作,但 ++a 是否真正原子依赖硬件指令,且需谨慎选择内存序以平衡正确性与性能。

std::atomic 是什么,它真能帮你避开互斥锁?
std::atomic 是 C++11 引入的模板类,用来对单个变量做「不可分割」的读-改-写操作。它不等于“无锁编程”的全部,但确实是无锁数据结构最基础的砖块。关键点在于:它只保证单个对象的原子性,不是魔法——比如对 std::atomic<int></int> 的 load()、store()、fetch_add() 是原子的;但 a = b + c(哪怕 a/b/c 都是 atomic)依然不是原子操作,中间可能被中断。
哪些操作真正原子?哪些只是“看起来像”?
常见误区是以为 ++a(其中 a 是 std::atomic<int></int>)天然线程安全——其实它依赖底层是否提供对应指令。x86 上 fetch_add(1) 通常编译为 lock xadd,是原子的;但 ARMv7 没有原生 fetch_add,可能退化为 LL/SC 循环(即 compare_exchange_weak 重试),失败时需手动处理。
-
load()和store()默认用memory_order_seq_cst,强顺序,但开销略大 -
fetch_add()、fetch_sub()、compare_exchange_weak()等才是构建无锁逻辑的核心,必须显式检查返回值 -
operator=调用的是store(),operator++调用的是fetch_add(1),但它们的内存序仍受模板参数影响,别默认
为什么直接用 std::atomic 做计数器没问题,但做队列就大概率出错?
因为无锁队列要同时维护 head/tail 两个指针,并保证「取节点 + 移动 head」这一连串动作的原子性——而 std::atomic 不支持多字段原子更新。你不能靠两个 std::atomic<node></node> 安全实现一个 lock-free queue,必须用 compare_exchange_weak() 配合状态重试,或引入 ABA 问题防护(如带版本号的指针)。
典型错误写法:
立即学习“C++免费学习笔记(深入)”;
Node* old_head = head.load(); Node* next = old_head->next; head.store(next); // ❌ 中间 old_head 可能已被其他线程释放
正确做法是用循环 + compare_exchange_weak() 验证 head 未变再更新:
Node* expected = head.load();
Node* desired;
do {
desired = expected->next;
} while (!head.compare_exchange_weak(expected, desired));内存序(memory_order)选错会导致什么?
选太松(如 memory_order_relaxed)可能让编译器或 CPU 重排指令,导致其他线程看到「部分更新」的状态;选太紧(如全用 memory_order_seq_cst)又会抑制优化,尤其在 ARM/PowerPC 上性能损失明显。
- 计数器累加常用
memory_order_relaxed(只要数值不错就行) - 标志位(如
ready)用memory_order_acquire(load)+memory_order_release(store)配对,确保其前后的内存访问不跨序 -
compare_exchange_weak()默认是seq_cst,但若你已用 acquire/release 控制好依赖,可显式降级为memory_order_acq_rel
真正难的从来不是写对一个 fetch_add,而是理清多个原子变量之间的同步关系和数据依赖——这里漏掉一个 acquire,整个无锁结构就可能在某次内核升级后突然失效。











