6.5. atomic

未匹配的标注

在之前的源码分析中,我们有多次看到atomic的出现,今天不妨我们看一下atomic的源码。
PS:解读的源码,如无特别说明,版本为1.15.6


我们看一下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
}

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
}

参考


关注和赞赏都是对笔者最大的支持
关注和赞赏都是对笔者最大的支持

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~