std::atomic 初始化必须显式,因默认构造不保证值为0;开关类逻辑需 release/acquire 内存序,计数器可用 relaxed;fetch_add 等比 operator++ 更可控;仅 std::atomic_flag 保证 lock-free。

std::atomic 初始化必须显式,不能靠默认构造
很多刚用 std::atomic 的人会写 std::atomic<int> counter;</int> 然后直接 counter++,结果在某些编译器(尤其是未开启优化时)触发未定义行为——因为 std::atomic 的默认构造不保证内部值为 0,它只是“不初始化”,内存内容是随机的。
正确做法永远显式初始化:
std::atomic<int> counter{0}; // ✅ 推荐
std::atomic<int> counter = 0; // ✅ 也行,但语义稍弱- 内置类型(
int、bool、指针等)支持{}列表初始化,最安全 -
std::atomic<:string></:string>不允许,因为std::string非平凡,std::atomic只支持 trivially copyable 类型 - 自定义结构体想原子化?先确保它满足
std::is_trivially_copyable_v<t></t>,否则编译失败
load/store 用 memory_order_relaxed 就够了?看场景
很多人一上来就用 .load(std::memory_order_relaxed) 和 .store(42, std::memory_order_relaxed),觉得“反正快”。但 relaxed 模式下,编译器和 CPU 都可能重排指令,导致逻辑错乱。
典型踩坑场景:用原子变量当“开关”却忘了同步语义:
立即学习“C++免费学习笔记(深入)”;
std::atomic<bool> ready{false};
int data = 0;
<p>// 线程 A
data = 42;
ready.store(true, std::memory_order_relaxed); // ❌ 危险!data 写入可能被延后</p><p>// 线程 B
while (!ready.load(std::memory_order_relaxed)) { /<em> spin </em>/ }
std::cout << data << "\n"; // 可能输出 0- 开关类逻辑(如初始化完成标志)必须用
std::memory_order_release(store) +std::memory_order_acquire(load)配对 - 计数器累加(如引用计数)、单纯统计(如请求总数)用 relaxed 完全没问题
-
std::memory_order_seq_cst是默认行为,最安全但有性能代价;x86 上多数操作天然满足,ARM/AArch64 上开销明显
operator++ 和 fetch_add 的区别不只是写法
counter++ 看起来简洁,但它其实是 fetch_add(1) + 返回旧值,而 counter.fetch_add(1) 明确返回旧值。问题在于:很多人误以为 ++counter(前缀)更高效,其实两者底层指令完全一样,都是 read-modify-write(RMW)操作。
真正影响性能的是是否需要旧值:
- 只关心更新后状态(如“第 N 个请求”),用
counter.fetch_add(1, std::memory_order_relaxed) + 1或直接++counter都行 - 需要原子地读-改-判(比如实现无锁栈 pop),必须用
fetch_*系列,因为operator=、operator++不提供对旧值的可控访问 - 注意
fetch_sub、fetch_and等不是所有平台都支持全部内存序;例如 ARM 上fetch_xor可能退化为锁总线,查std::atomic<t>::is_always_lock_free</t>更稳妥
std::atomic_flag 是唯一保证 lock-free 的类型
其他 std::atomic 类型(哪怕 std::atomic<bool></bool>)在某些平台可能实际走互斥锁模拟,尤其在嵌入式或老编译器上。只有 std::atomic_flag 被标准强制要求 lock-free,且只提供 test_and_set() 和 clear() 两个操作。
它适合做最轻量的自旋锁原语:
std::atomic_flag guard = ATOMIC_FLAG_INIT;
<p>while (guard.test_and_set(std::memory_order_acquire)) {
// 自旋等待
}
// 临界区
guard.clear(std::memory_order_release);-
ATOMIC_FLAG_INIT是唯一合法初始化方式,不能用{}或= {} - 它没有
load()方法,只能靠test_and_set()一次读+设,所以不适合做状态标志位(比如“是否完成”),那是std::atomic<bool></bool>的事 - 如果看到
std::atomic<bool>.is_lock_free()</bool>返回 false,别硬用——换std::atomic_flag或直接上std::mutex
原子操作不是“加个 atomic 就线程安全”,关键在内存序选择和操作语义匹配。最容易被忽略的是:同一变量的多次原子访问之间,是否构成同步关系——这不取决于你用了什么函数,而取决于你选的 memory_order 和它们在代码中的相对位置。







