惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
MyScale Blog
MyScale Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
人人都是产品经理
人人都是产品经理
C
Check Point Blog
宝玉的分享
宝玉的分享
B
Blog RSS Feed
博客园 - 三生石上(FineUI控件)
量子位
Martin Fowler
Martin Fowler
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
IT之家
IT之家
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
J
Java Code Geeks
The Cloudflare Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
腾讯CDC
P
Proofpoint News Feed
美团技术团队
H
Help Net Security
B
Blog
博客园_首页

Jiajun的技术笔记

你好,2026! TiDB 源码阅读(六):TiDB Coprocessor 源码解析 性能优化的核心思想 TiDB 源码阅读(五):索引 TiDB 源码阅读(四):AST、逻辑计划、物理计划 CockroachDB Serverless Architecture podman 无故退出 Cursor Control-L (CTRL-L) Keyboard Shortcuts in Terminal Replace docker with podman Using xmonad with xfce4 A RC script for freebsd frpc 自己动手写一个k8s controller AI 会取代你的(编程)岗位吗? 自建DERP服务器提升Tailscale连接速度(使用Nginx转发) 自动升级Docker容器 再读《程序员修炼之道-从小工到专家》 让浏览器下载文件 再读《软件随想录》/《黑客与画家》/《软技能》 HTTP 压力测试中的 Coordinated Omission 2的补码 编程语言中的 context 是什么? flutter macOS 构建出错 Flatpak 使用小记 PostgreSQL 当MQ来使用 Clash 结合 工作VPN 的网络设计 使用 PostgreSQL 搭建 JuiceFS PostgreSQL 配置优化和日志分析 有GitHub Copilot?那就可以搭建你的ChatGPT4服务 窗口函数的使用(以PG为例) 读《为什么学生不喜欢上学》
Golang CAS 操作是怎么实现的
Jiajun Huang · 2024-03-10 · via Jiajun的技术笔记

在Go语言中,CAS(Compare and Swap) 操作一般都是通过 atomic 操作来实现的,我们来探究一下底层是怎么实现的。

我们以 CompareAndSwapInt32 为例,首先找到源码,位于 doc.go

// CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value.
// Consider using the more ergonomic and less error-prone [Int32.CompareAndSwap] instead.
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)

在Go语言中,这种只有函数声明,没有函数实现的,通常意味着函数的实现在Go汇编中。在 asm.s 中可以找到定义:

TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0
	JMP	runtime∕internal∕atomic·Cas(SB)

然后跟着路径找 internal/atomic/atomic_amd64.s

// bool Cas(int32 *val, int32 old, int32 new)
// Atomically:
//	if(*val == old){
//		*val = new;
//		return 1;
//	} else
//		return 0;
TEXT ·Cas(SB),NOSPLIT,$0-17
	MOVQ	ptr+0(FP), BX
	MOVL	old+8(FP), AX
	MOVL	new+12(FP), CX
	LOCK
	CMPXCHGL	CX, 0(BX)
	SETEQ	ret+16(FP)
	RET

我们来看下这段汇编代码的意思,以下解释来自ChatGPT:

TEXT ·Cas(SB),NOSPLIT,$0-17

这行代码定义了一个名为 Cas 的函数,SB 是一个汇编器符号,表示当前包的起始地址。 NOSPLIT 表示该函数不会发生栈的分裂,$0-17 表示函数没有输入参数,但有 17 个字节的输出参数。

MOVQ	ptr+0(FP), BX

这行代码将函数的第一个输入参数 ptr 的值加载到寄存器 BX 中。

MOVL	old+8(FP), AX

这行代码将函数的第二个输入参数 old 的值加载到寄存器 AX 中。

MOVL	new+12(FP), CX

这行代码将函数的第三个输入参数 new 的值加载到寄存器 CX 中。

LOCK

这行代码是一个前缀指令,用于告诉处理器后面的指令是原子操作,需要获取总线锁。

CMPXCHGL	CX, 0(BX)

这行代码使用 CMPXCHG 指令进行比较和交换操作。它比较内存地址 0(BX) 处的值与寄存器 CX 的值是否相等,如果相等,则将寄存器 CX 的值写入内存地址 0(BX) 中。

SETEQ	ret+16(FP)

这行代码根据 CMPXCHG 指令的结果设置标志位,如果比较和交换成功,则将标志位设置为 1,否则设置为 0。

RET

这行代码表示函数的返回。

总的来说,这段汇编代码实现了一个 CAS 操作,它比较内存地址中的值与期望值是否相等,如果相等,则将新的值写入内存地址中,并返回操作是否成功。这个 CAS 操作使用了 CMPXCHG 指令来实现原子的比较和交换操作。

其实说到底,Golang的CAS操作,就是依靠 LOCK + CMPXCHGL 来实现的,其他的语言也是一样的,需要依赖CPU提供这种底层 能力,才能够真正的做到 CAS。