atomic
在之前的源码分析中,我们有多次看到atomic的出现,今天不妨我们看一下atomic的源码。
我们看一下atomic的源码文件:
这里说明一下,以.s为后缀的是汇编语言源代码文件,你可以并不懂汇编,没有关系。
主要看下asm.s,看一看到里面有调用runtime ∕ internal ∕ atomic,我们前去看一下这个文件夹,其中有个文件atomic_wasm.go。
atomic提供的是原子操作,atomic包中支持六种类型
- int32
- uint32
- int64
- uint64
- uintptr
- unsafe.Pointer
对于每一个类型,支持5种操作,我们以int32分别说明下这些操作:
SwapX
// 原子性的将新值保存到*addr并返回旧值。
func SwapInt32(addr *int32, new int32) (old int32)
// 源码
func Xchg(ptr *uint32, new uint32) uint32 {
old := *ptr
*ptr = new
return old
}
CompareAndSwapX
// 原子性的比较*addr和old,如果相同则将new赋值给*addr并返回真。
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
// 源码
func Cas(ptr *uint32, old, new uint32) bool {
if *ptr == old {
*ptr = new
return true
}
return false
}
AddX
// 原子性的将val的值添加到*addr并返回新值
func AddInt64(addr *int64, delta int64) (new int64)
// 源码
func Xadd(ptr *uint32, delta int32) uint32 {
new := *ptr + uint32(delta)
*ptr = new
return new
}
LoadX
// 原子性的获取*addr的值
func LoadInt32(addr *int32) (val int32)
// 源码
func Load(ptr *uint32) uint32 {
return *ptr
}
StoreX
// 原子性的将val的值保存到*addr
func StoreInt32(addr *int32, val int32)
// 源码
func Store(ptr *uint32, val uint32) {
*ptr = val
}
源码其实比较简单了,我就不过多说明了。
atomic.Value
另外,atomic对支持的类型做了扩展,atomic.Value被设计用来存储任意类型的数据。
type Value struct {
v interface{}
}
为了方便,定义了一个ifaceWords类型,它的作用是将interface{}
类型分解,得到其中的两个字段,作为interface的内部表示格式,typ代表原始类型,data代表真正的值。
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
提供了Store和Load两个方法。
Store
func (v *Value) Store(x interface{}) {
// x为nil,直接panic
if x == nil {
panic("sync/atomic: store of nil value into Value")
}
// 将现有的值和要写入的值转换为ifaceWords类型,这样下一步就能获取到它们的原始类型和真正的值
vp := (*ifaceWords)(unsafe.Pointer(v))
xp := (*ifaceWords)(unsafe.Pointer(&x))
for {
// 获取现有的值的type
typ := LoadPointer(&vp.typ)
// 如果typ为nil说明这是第一次调用Store
if typ == nil {
// 如果是第一次调用,就占住当前的processor,不允许其他goroutine再抢,runtime_procPin方法会先获取当前goroutine
runtime_procPin()
// 使用CAS操作,先尝试将typ设置为^uintptr(0)这个中间状态
// 如果失败,则证明已经有别的goroutine抢先完成了赋值操作
// 那它就解除抢占锁,继续循环等待
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
runtime_procUnpin()
continue
}
// 如果设置成功,就原子性的更新对应的指针,最后解除抢占锁
StorePointer(&vp.data, xp.data)
StorePointer(&vp.typ, xp.typ)
runtime_procUnpin()
return
}
// 如果typ为^uintptr(0)说明第一次写入还没有完成,继续循环等待
if uintptr(typ) == ^uintptr(0) {
continue
}
// 如果要写入的类型和现有的类型不一致,则panic
if typ != xp.typ {
panic("sync/atomic: store of inconsistently typed value into Value")
}
// 更新data,跳出循环
StorePointer(&vp.data, xp.data)
return
}
}
Load
func (v *Value) Load() (x interface{}) {
// 将*Value指针类型转换为*ifaceWords指针类型
vp := (*ifaceWords)(unsafe.Pointer(v))
// 原子性的获取到v的类型typ的指针
typ := LoadPointer(&vp.typ)
// 如果没有写入或者正在写入,先返回,^uintptr(0)代表过渡状态,这和Store是对应的
if typ == nil || uintptr(typ) == ^uintptr(0) {
return nil
}
// 原子性的获取到v的真正的值data的指针,然后返回
data := LoadPointer(&vp.data)
xp := (*ifaceWords)(unsafe.Pointer(&x))
xp.typ = typ
xp.data = data
return
}
PS:解读的源码,如无特别说明,版本为1.15.6
参考:
atomic
https://blog.puresai.com/2021/02/13/atomic/