go 进阶 sync相关: 一. sync.atomic​ 原子操作

一. sync.atomic 基础

  1. atomic 包中提供许多基本数据类型的原子操作,主要可以分为下面几类:
  1. 原子交换
  2. CAS
  3. 原子加法
  4. 原子取值
  5. 原子赋值
  6. Value
  1. 原子操作相关方法,将 new 存储到地址 addr 并返回该地址上原来的值
func SwapInt32(addr *int32, new int32) (old int32)
func SwapInt64(addr *int64, new int64) (old int64)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)
  1. CAS操作相关方法,拿 addr 上的值和 old 比较,如果相等,就把 new 存储到 addr
func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)
  1. CAS实现轻量级锁示例
func casADD() {
    defer w.Done()
    for i := 0; i < 10000; i++ {
        for old := a; !atomic.CompareAndSwapInt64(&a, old, old + 1);  {
            old = a
        }
    }
}
  1. 原子加法,是给原来 addr 地址上的值加上 delta, 并返回最新的值,注意如果使用 AddUint64 执行 x - c 需要执行 AddUint64(&x, ^uint64(c-1)), 所以原子的 x – 可以写为 AddUint64(&x, ^uint64(0)), uint32 和 AddUint32() 同理
func AddInt32(addr *int32, delta int32) (new int32)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)
  1. 原子取值,从地址 addr 取值并返回
func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)
  1. 原子赋值,将 val 存储到地址 addr
func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

sync.atomic.Value

  1. 上面提供大量的针对数值和指针类型的原子操作相关方法,为了扩大原子操作的范围,在 Go 1.4 的加入了sync.atomic.Value
type Value struct {
    v interface{}
}
  1. value使用示例: value 使用起来非常简单可以把它当作一个容器,可以将值存入该容器中,然后通过容器取值,通过容器保证了原子性的,提供了Load() 和 Store() 两个方法
  1. Load(): 安全地从内存中读取值,
  2. Store(): 将值安全地存入内存
type S struct {
    a int
}

func main() {
    var v atomic.Value
    s := S{1}
    v.Store(s)
    p := v.Load()
    fmt.Println(p.(S).a)
}

二. sync.atomic 源码分析

1. ifaceWords

  1. 了解sync.atomic底层,首先要了解Go底层提供的一个私有的结构体 ifaceWords,内部含 typ 和 data 两个指针类型属性,前者表示值的真实类型,后者表示值的“值”,通过unsafe.Pointer 转换成 ifaceWords, 可以得到 interface{} 真实的类型和值
type ifaceWords struct {
    typ  unsafe.Pointer
    data unsafe.Pointer
}
  1. ifaceWords用于表示接口类型变量的底层数据结构,在 sync/atomic 包中通过ifaceWords,实现对接口类型变量进行原子性操作的功能,相对于使用 Eface 和 Iface 类型来存储接口类型变量的值,使用 ifaceWords 可以带来更高的效率和更小的内存开销。因为 ifaceWords 直接存储了具体类型信息和实际数据指针,而不需要像 Eface 和 Iface 一样再额外封装一层。因此,在某些场景下,使用 ifaceWords 非常适合进行内存优化和性能优化

2. Store()添加

  1. 在通过Store()存储数据时,底层的执行顺序:
  1. 先判断存储的数据是否为nil
  2. 将原数据与现在要存储的数据强转为ifaceWords类型
  3. 通过LoadPointer()获取到原值的真实数据类型,如果为nil说明第一次存储,先调用runtime_procPin()禁止抢占,并且防止GC执行,然后调用CompareAndSwapPointer()比较并交换进行原子更新,如果存储失败则continue自旋重试
  4. 如果不是第一次存储数据,进行数据类型校验,校验成功后调用StorePointer(),把新值 x 的类型和值存储在 v 的地址上
func (v *Value) Store(x interface{}) {
	//1.判断是否为nil
    if x == nil {
        panic("sync/atomic: store of nil value into Value")
    }
    //2.将原值与现在添加的值强转为ifaceWords类型
    //将Value类型的对象v转成ifaceWords类型的对象,因为v的底层结构与ifaceWords是相同的
    vp := (*ifaceWords)(unsafe.Pointer(v))
    //将入参对象x转成ifaceWords类型的对象,x为interface{}类型,底层结构与ifaceWords是相同的
    xp := (*ifaceWords)(unsafe.Pointer(&x))
	
	//3.自旋
    for {
    	//获取原值的真实数据类型
        typ := LoadPointer(&vp.typ)
        //如果原值为nil,说明是第一次存储值 
        if typ == nil {          
        	 //禁止抢占,防止 GC 看到 unsafe.Pointer(^uintptr(0)) 这个奇怪的类型
            runtime_procPin()   
            //通过原子性操作,存储数据,如果存储失败continue重试
            if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
            	//释放禁止抢占
                runtime_procUnpin()
                continue         // 比较不通过,说明有别人在执行赋值,自旋等待
            }
            //分别将入参中的type和data存储到v中,注意这里是先存储data然后存储typ,
            //因为程序以typ是否设置完成,来判断整个存储操作全部完成
            StorePointer(&vp.data, xp.data)   // 设置新置
            StorePointer(&vp.typ, xp.typ)     // 设置类型
            runtime_procUnpin()
            return
        }
        //该判断返回true,说明赋值没结束,自旋等待
        if uintptr(typ) == ^uintptr(0) {       
            continue
        }
        
         //4.当执行到此处说明不是第一次存储,判断此次添加的数据,类型与原始值是否相同
        if typ != xp.typ {
            panic("sync/atomic: store of inconsistently typed value into Value")
        }
        //把 x 写入 v
        // 只有第一次需要设置 tpy, 后面只需要设置 data 
        StorePointer(&vp.data, xp.data)       
        return
    }
}

3. Load()获取

  1. Load相对简单, 通过 ifaceWords 拿到 v 的真实类型,如果 v 中没有存值或正在写入,他会直接返回 nil,否则就把 v.data 和 v.typ 重新组装成 interface{} 返回
func (v *Value) Load() (x interface{}) {
    vp := (*ifaceWords)(unsafe.Pointer(v))
    typ := LoadPointer(&vp.typ)
    if typ == nil || uintptr(typ) == ^uintptr(0) {
        // First store not yet completed.
        return nil
    }
    data := LoadPointer(&vp.data)
    xp := (*ifaceWords)(unsafe.Pointer(&x))
    xp.typ = typ
    xp.data = data
    return
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值