cmpxchg是CPU硬件直接支持的原子指令,通过LOCK前缀触发总线或缓存锁定(如MESI),在单步内完成“读值—比对—条件写入”,确保多核下内存操作不可中断、不被干扰,从而支撑CAS及无锁编程。

cmpxchg 是怎么做到“一步到位”的
它不是靠软件逻辑拼出来的,而是 CPU 硬件直接支持的一条指令。执行时,CPU 会把一次“读值—比对—写新值(或回填旧值)”打包成不可中断的单步操作。关键在于:这个过程不被其他线程、核心甚至 DMA 设备打断。在单核上,指令天然原子;在多核下,必须靠 LOCK 前缀 触发总线锁定或缓存一致性协议(如 MESI),才能保证内存位置的独占访问。
寄存器和内存之间如何配合
指令格式是 cmpxchg mem, reg,其中:
- 隐含比较源始终是 EAX(或 AL/AX/RAX,取决于位宽),它存的是你“期望的旧值”
- mem 是要更新的内存地址(比如一个共享变量的地址)
- reg 是你要写入的新值所在的通用寄存器(不能是立即数,也不能是段寄存器)
执行时先比 EAX 和 mem 的值:相等就用 reg 覆盖 mem,ZF=1;不等就把 mem 当前值读回 EAX,ZF=0。整个过程只访问一次内存位置(现代 CPU 会用缓存行锁定代替总线锁),但逻辑上完成了“条件写”所需的全部判断与动作。
为什么它能支撑无锁编程
CAS(Compare-And-Swap)语义就靠它落地。比如实现一个无锁计数器:
- 线程读取当前值到 EAX
- 计算新值(EAX+1),放进 EBX
- 执行 lock cmpxchg [counter], ebx
- 检查 ZF:为 1 表示成功更新;为 0 就说明期间被别人改过,需重试
没有锁等待、没有上下文切换开销,失败只损失一次尝试——这正是自旋锁、无锁栈、无锁队列等结构的底层支柱。
实际使用中容易踩的坑
几个关键约束必须牢记:
- 目的操作数 必须是内存地址或寄存器,但用于并发场景时几乎总是内存
- 源操作数 只能是寄存器,不能直接写立即数(如
cmpxchg [x], 5非法) - 跨平台安全起见,必须加 LOCK 前缀,即使文档说“某些情况可省”,也不建议依赖
- 操作宽度要匹配:8/16/32/64 位对应 AL/AX/EAX/RAX 和相应宽度的内存与寄存器
- 不能用于 386 或更早 CPU,最低要求是 486

















