使用场景
多个goroutine里面安全访问共享变量
核心实现逻辑
- 通过系统的CPU指令来保证对变量的原子操作,如i386的LOCK指令
atomic实现
-
代码主要包含如下功能,实际为三种类型,通用类型Value、通用型函数、对第一种类型的封装
- 通用型函数
- Swap函数功能:将数据原子替换
- CompareAndSwap: 对比数据后将数据原子替换
- Add:原子增加数据
- Load:原子加载数据
- Store:原子存储数据
- 通用型函数,也是对第一种类型的封装
- 更加通用的实现,Struct Value:该struct也实现了上面的各种函数,Value里的参数包含v any
- 对第一种类型的封装,使用更加方便明确,通过noCopy声明不能内嵌
- type.go里面定义一些常用类型的实现,Bool、Pointer(泛型)、Int32、Int64、Uint32、Uint64、Uintptr,实际调用的是上面的Swap等一系列函数
- 通用型函数
-
代码实现
- 用户调用,调用doc.go
- 实际调用同目录下的汇编代码asm.s
- 汇编代码asm.s调用到runtime∕internal∕atomic下的汇编代码
- AddInt32对应runtime∕internal∕atomic·Xadd
- 对应runtime/internal下的atomic_{arch}.go文件,可以通过arch命令查看当前的系统是什么
- runtime/internal下的atomic_{arch}.go对应runtime/internal下的atomic_{arch}.s文件
- 不同的系统,有不同的实现
-
函数实现例子
- sync/doc.go,里面为用户调用golang的入口
func AddInt32(addr *int32, delta int32) (new int32)
- sync目录有个asm.s,里面有对应上面函数的汇编实现
TEXT ·AddInt32(SB),NOSPLIT,$0 JMP runtime∕internal∕atomic·Xadd(SB)
- 对应runtime∕internal∕下的atomic_{arch}.go函数
func Xadd(ptr *uint32, delta int32) uint32
- 对应runtime∕internal∕下的atomic_{arch}.s函数,主要是LOCK指令,保证原子性,下面是i386系统的代码
TEXT ·Xadd(SB), NOSPLIT, $0-12 MOVL ptr+0(FP), BX MOVL delta+4(FP), AX MOVL AX, CX LOCK XADDL AX, 0(BX) ADDL CX, AX MOVL AX, ret+8(FP) RET
思考
- 已经有Mutex,为什么要atomic?
使用场景不太一样,Mutex主要是对一段代码的加锁,atomic主要是对共享变量的原子操作。
参考
- https://kunkkawu.com/archives/shen-ru-li-jie-golang-de-atomic-yuan-zi-cao-zuo