我在简历里写了“熟悉 atomic”,但我不希望把原子变量讲成一堆细节堆砌。更理想的状态是:面试时能在 1~2 分钟内把 atomic 的核心价值讲清楚,并且能顺着回答两个常见追问:
- atomic 是什么,解决什么问题
- 编译器如何处理 atomic,机器码和普通变量有什么区别
- 运行时执行到这些机器码时,CPU、cache、memory 里发生了什么
这篇文章就是我对这三块内容的整理。
1. atomic 是什么
原子变量(std::atomic<T>)可以概括为两层能力:
1.1 它是一种变量类型,提供“不可撕裂”的访问
atomic 的读写是原子的,不会出现“读到一半新、一半旧”的撕裂状态。对一个 atomic 变量的读取,结果只能是:
- 旧值
- 新值
不会是中间态。
1.2 它还能建立跨线程的可见性与先后关系
atomic 不只是“读写不撕裂”,更重要的是它提供了并发下的可见性与顺序约束。这一点由 **memory order(内存序)**控制,核心作用包括:
- 哪些操作不能被重排序
- 一个线程的写什么时候对另一个线程可见
- 用怎样的“同步边”把两段代码连起来(经典就是
release / acquire)
2. 编译器层面:为什么 atomic 会被特殊对待
2.1 编译器为什么要特殊处理 atomic
普通变量如果被多个线程并发读写,会发生 data race,在 C++ 里这会导致 UB(未定义行为) 。
UB 的关键含义是:
编译器可以基于“单线程假设”做非常激进的优化,导致并发下的行为无法预测。
atomic 是语言层面的承诺:
即使并发访问,行为仍然定义良好。因此 atomic 本质上是一个跨线程通信点,编译器必须遵守内存序语义。
2.2 编译器保证三件事
-
原子性(Atomicity)
- 单次访问不可撕裂(只能读到旧值或新值)
- RMW 操作不可被拆分(不能变成
load + op + store)
-
顺序(Ordering)
- 按
memory_order限制编译器重排 - 必要时生成更强的指令或屏障维持顺序语义
- 按
-
可见性(Visibility)
- 规定一个线程的写何时对另一个线程可见
- 通过
release / acquire / seq_cst等形成同步边
2.3 编译器对优化的限制
一句话总结:
编译器不能把 atomic 当普通变量“随便缓存、合并、删掉、乱重排”。
常见约束包括:
- 不能长期缓存到寄存器
- 不能把多次 atomic
load合并成一次 - 不能把 atomic
store当“无用写”消掉 - 不能把 atomic 与周围内存访问随意重排
2.4 生成机器码:和普通变量的区别
-
atomic load/store
- 在某些架构上“看起来还是
mov/ldr/str”(尤其 x86) - 差别不一定是指令长相,而是:编译器不敢乱优化 + 必要时会加屏障/更强语义
- 在某些架构上“看起来还是
-
atomic RMW / CAS(差异最明显)
- 常用专门的原子指令或原子序列
- x86:常见
lock前缀相关指令 - ARM:常见
ldxr/stxr(独占对)+ 必要的 barrier
2.5 编译器层总结
- 普通变量:编译器只保证单线程语义,并发读写可能 UB
- atomic:编译器按内存序把它当成同步点,限制优化,并在需要时生成原子指令/屏障或退化到库/锁来保证语义
3. CPU 与运行时层面:机器码执行时发生了什么
这一层我希望能讲清一件事:
atomic 在运行时并不是“魔法”,它落地在 CPU 的 cache/coherence 与 barrier 语义上。
3.1 执行与调度:OS 的参与点
- OS 调度线程到不同 CPU core 运行,线程可能发生迁移
- OS 不负责实现原子性,原子语义主要由 CPU 指令与硬件内存模型保证
- OS 主要参与线程切换与阻塞机制,atomic 自旋属于用户态忙等
3.2 内存访问路径:CPU 实际如何读写
- 指令在 core 上执行,数据访问先到寄存器与本核 cache hierarchy(L1/L2/L3)
store通常先进入 store buffer,再逐步对外可见load优先命中本核 cache,不命中再向更高层级或 DRAM 获取- 共享变量所在的 cache line 是跨核竞争与同步的基本单位
4. 面试时的“可复述主线”
我用三句话把它串起来:
- atomic 在语言层保证并发访问定义良好,提供原子性与可见性/顺序语义。
- 编译器把 atomic 当作跨线程通信点,限制优化并按内存序生成相应的原子指令或屏障。
- 运行时这些指令落到 CPU 的 cache coherence、store buffer 与 barrier 上,通过对 cache line 的协调实现原子更新与发布-获取可见性。