std::atomic 禁止拷贝构造和赋值以保障原子性,必须用 load/store 或 fetch_add/compare_exchange 等原子操作;compare_exchange_weak 因伪失败更高效,推荐循环使用;内存序需按需选择,错用易致数据竞争;T 需 trivially copyable 且尺寸适配,否则退化为加锁。

std::atomic 为什么不能直接赋值或拷贝
因为 std::atomic 禁止了拷贝构造和拷贝赋值操作符,这是为保证原子性不被意外破坏。试图写 std::atomic 或 auto c = a; 会触发编译错误:use of deleted function。
- 必须用
.load()读取当前值(可指定内存序) - 必须用
.store(x)写入新值(同样支持内存序) - 需要“读-改-写”时,优先用
.fetch_add()、.compare_exchange_weak()等原子成员函数
compare_exchange_weak 和 compare_exchange_strong 怎么选
两者都实现 CAS(Compare-and-Swap),但 compare_exchange_weak 允许伪失败(spurious failure):即使预期值匹配,也可能返回 false。这在某些平台(如 x86 上少见,ARM 上较常见)由底层指令限制导致。
- 循环中使用时,
weak版本通常性能更好,推荐作为默认选择 -
strong版本保证“值不匹配才失败”,适合不能容忍重试逻辑的场景(如单次尝试关键路径) - 务必配合 do-while 循环使用,避免漏掉失败重试
std::atomiccounter{0}; int expected = counter.load(); do { int desired = expected + 1; } while (!counter.compare_exchange_weak(expected, desired));
内存序(memory order)不是可有可无的配置项
省略内存序参数(如只写 a.store(42))等价于 a.store(42, std::memory_order_seq_cst),即最强一致性。但多数场景不需要这么重的同步开销。
-
std::memory_order_relaxed:仅保证原子性,不约束前后内存访问顺序 —— 计数器、标志位常用 -
std::memory_order_acquire:用于读操作,确保之后的读/写不被重排到它前面 -
std::memory_order_release:用于写操作,确保之前的读/写不被重排到它后面 - acquire-release 配对可实现线程间同步,而无需全局顺序
错用内存序不会报错,但可能引发极难复现的数据竞争 —— 比如把 relaxed 误用于需同步的 flag 变量,会导致另一线程看到“部分初始化”的对象。
立即学习“C++免费学习笔记(深入)”;
std::atomic 对 T 的类型要求很实际
并非所有类型都能套 std::atomic。编译期会检查 T 是否为 trivially copyable,且大小不能超过平台支持的原子指令宽度(通常 ≤ 16 字节,x86-64 下一般 ≤ 8 字节)。
- 基本类型(
int、long long、指针)基本都 OK - 自定义结构体只有满足
std::is_trivially_copyable_v且不含虚函数、非平凡构造/析构时,才可能被接受 - 即使满足条件,若尺寸过大(如 32 字节 struct),GCC/Clang 会退化为内部加锁实现 —— 此时已不是“无锁”,且性能下降明显
- 不确定时,用
static_assert(std::atomic显式校验::is_always_lock_free)
无锁编程真正的门槛不在语法,而在对共享状态变更边界的精确刻画。一个 std::atomic_flag 能安全自旋,但若把它和非原子字段混在同一缓存行里,就可能因 false sharing 拖垮性能 —— 这类问题不会报错,只会让多核跑得比单核还慢。











