cmpxchg 与 CAS 循环
这页讲的是:有些并发更新不是简单加减一个数,而是“如果对象还保持旧样子,就把它换成新样子;如果已经被别人改过,就重新看局面再决定”。学这页的重点,是理解 cmpxchg 更像一种带条件的原子替换,它常把并发修改写成“尝试—失败—重试”的 CAS 循环。
这块是什么
cmpxchg 与 CAS 循环,讲的是 Linux 怎样在并发更新里先比较当前值是否还是自己预期的旧值,若是就原子地换成新值,若不是就说明局面已变,需要重新读取并再试一次。它关注的是“带前提条件的原子替换”,而不是单纯把某个计数加一减一。
可以把它理解成:你准备把白板上的 A 改成 B,但前提是白板上现在还真的是 A。如果已经被别人改成 C,就不能硬写 B,而要先重新理解现场再决定。
它负责什么
把条件判断和替换绑成一个整体
- 很多并发更新不能只改值,还要先确认“局面没变”
- cmpxchg 把比较旧值和写入新值捆成原子动作
- 避免中间被别人抢先改变状态
- 让条件式更新真正站得住
支撑失败后重试的无锁写法
- 如果条件不成立,不代表系统坏了
- 常见做法是重新读取状态再尝试
- 把并发竞争显式变成“失败就重算”的循环
- 让某些轻量更新不必总靠大锁保护
给状态机切换提供精细入口
- 很多状态跳转都只允许从特定旧状态进入新状态
- cmpxchg 非常适合表达这种“只在旧状态匹配时推进”
- 让状态机边界更清楚
- 避免两个执行者都以为自己成功切换了状态
为什么它不是“原子操作的花哨版本”
| 如果想得太简单 | 会怎样 | 真正关键在哪 |
|---|
| 觉得只是多一个 API 形式 | 会看不见它真正表达的是“只有前提还成立时才允许替换”。 | 它的价值在条件式原子更新,不在名字更复杂。 |
| 把失败当异常 | 会误解 CAS 循环的正常工作方式。 | 失败常是并发竞争的自然结果,重试才是设计的一部分。 |
| 觉得这样就完全无锁无脑更快 | 会漏掉高竞争下重试成本可能很高。 | CAS 循环更轻不等于永远更省。 |
| 只看单变量替换 | 会低估它经常承载的是状态机入口和对象发布边界。 | cmpxchg 常在更大协议里扮演关键门闩。 |
关键概念
| 概念 | 现在怎么理解 |
|---|
| cmpxchg | 先比较旧值是否匹配,再决定是否原子替换成新值的操作。 |
| CAS 循环 | 失败就重新读取状态、重新计算、再尝试替换的一类写法。 |
| 条件式更新 | 不是无条件写新值,而是只有局面仍符合预期才推进。 |
| 竞争失败 | 别人先一步改了值,自己这次尝试需要重来,并不等于系统出错。 |
| 状态门闩 | 很多状态切换都把 cmpxchg 当成“只有一个人能抢到入口”的门闩。 |
为什么重要
- 它解释了为什么很多轻量并发更新不只是“改值”,而是“确认局面还对,再改值”。
- 很多引用获取、状态发布、一次性初始化和轻量无锁路径都离不开这种思想。
- 理解这层后,你会更容易把 atomic operation、memory barriers 和状态机协议连起来。
- 它让你看到:并发正确性有时不在于先锁住一大片,而在于先抢到那个唯一合法的状态入口。
常见误解
- 误解一:CAS 失败就是 bug。实际上重试往往是它的本来工作方式。
- 误解二:有 cmpxchg 就不用再思考顺序语义。实际上它经常还要和屏障或发布协议配合。
- 误解三:它一定比锁更好。实际上高竞争、长重算路径下,CAS 循环也可能很难看。
它不负责什么
- 它不适合承载很长、很复杂、涉及多个变量的大协议。
- 它不自动解决对象生命周期问题,旧值、新值指向的对象是否还安全仍要别的规则保证。
- 它不等于所有无锁设计,很多更大语义还要额外组织。
和其他模块的关系
| 相关模块 | 关系 |
|---|
| atomic operations | cmpxchg 是更有条件语义的一类原子动作,比普通加减更常用于状态切换。 |
| memory barriers | 替换成功后,别人何时按什么顺序看到新状态和相关数据,常要继续看顺序语义。 |
| refcount_t | 很多“只有对象仍可持有时才安全加引用”的思路,都和条件式更新非常接近。 |
| 锁与无锁路径 | 它常出现在试图避免大锁、但又要保住最小正确性边界的设计里。 |
读完这页后,你应该能回答
- 为什么 cmpxchg 真正擅长的是“比较旧局面是否仍成立,再决定是否替换”?
- 为什么 CAS 循环把“失败—重试”当成正常控制流,而不是异常情况?
- 为什么 cmpxchg 常常扮演状态机入口门闩,而不是单纯更快的自增自减?
后面适合继续问:cmpxchg 和 atomic_add_return 最容易混在哪?高竞争下 CAS 循环为什么可能比上锁还难看?对象指针替换为什么常要把 cmpxchg 和生命周期规则一起看?