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

2023-11-08

一. 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
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • Visual Studio 2010 部署QT4.8.4

    做GUI界面的设计时 目前已不再拘泥于VS的MFC框架 有很多开源的工具 本例以QT与VS2010的整合为例 演示环境搭建 后续将会用QT做VT的开发工作 第一步 下载QT和QT与VS的插件 在VS2010下以及结合VTK的情况 网络上经网
  • 让 Home 页面离开的时候保持原来的状态

    思路 1 用 keep alive 让页面不要销毁
  • 对有验证码的后台网页进行爆破-captcha-killer-modified

    对有验证码的后台网页进行爆破 captcha killer modified 实例 以pikachu靶场为目标 攻击目标url http xxxx pikachu master vul burteforce bf server php xx
  • PAT B1072 开学寄语

    算法思想 用数组标记应要查缴的物品mark 物品编号 1 在输入学生信息的同时判断该物品是否要查缴 因格式问题用了另一个数组保存该学生要查缴的物品编码 最后再输出该学生的名字和查缴物品编号 后来看了柳神的代码 不用这么麻烦 直接在输出前加括
  • HDU2085核反应堆

    Time Limit 1000 1000 MS Java Others Memory Limit 32768 32768 K Java Others Total Submission s 22891 Accepted Submission
  • PID控制原理说明与图解

    PID是比例 积分 微分的简称 一般常用于温度 速度等标量的控制 应用PID控制的系统存在对这些数值进行感测的传感器 又存在一个设定的目标值 这一种系统称为闭环控制系统 closed loop control system 系统中三个数值的
  • 工作能力构架框架图——我对工作的理解

    工作架构图 最近对工作内容进行了一次review 归纳如下 做简单地分享 job层 job层是我们最容易理解的一层 即我们平时的工作title 可能是销售 售前 产品等等 那么做产品的人能不能做售前呢 我想 肯定可以 因为在人力资源市场上
  • 解决eclipse出现launch failed,binary not found 的问题

    解决eclipse出现launch failed binary not found 的问题 仅作为解决问题的记录 问题描述 1 之前一直用eclipse写C 文件 后面学了java就用eclipse写了一些 java文件 但是偶然一次想要再
  • ValueError: column index (256) not an int in range(256)

    pandas 保存为xls时最大列为256 改为保存csv格式可避免该限制 解决方法 pd DataFrame in shp pre to csv 各区站点 file 14 17 csv
  • 软件开发管理参考网站

    BDD http en wikipedia org wiki Behavior Driven Development Scrum http zh wikipedia org zh Scrum UserCase http www hudong
  • Kubernetes 资源管理:最大化集群性能

    今天是 DevOps云学堂 与你共同进步的第 54天 Kubernetes 资源管理是部署和管理容器化应用程序的一个关键方面 它允许管理员控制系统不同部分之间计算资源的分配 例如CPU 内存和存储 有效的资源管理可确保应用程序获得正确运行所
  • Latex 字体的设置

    一 字体族的设置 1 说明 1 字体命令 textrm Roman Family 在花括号内的字体都是Roman Family 字体 2 字体声明 rmfamily Roman Family 该命令下面的字体都是是Roman Family字
  • Vim几种跳转方式

    Vim几种跳转方式 ps 以下时我常用的一些跳转指令 用于参考和复习记忆 还有一些后续会更新 文件内跳转 移动光标 普通模式下左h 右l 上k 下j 可以使用数字 hlkj 实现跳跃式移动 字符间跳转 跳转到当前字符的前面 普通模式下i 跳
  • CesiumJS三维案例

    WebGL近几年越来越被人们所关注 但是二三维开发难度也比普通web要高出许多 不管我们是在在开发或者是学习过程中 往往需要耗费大量的时间去查阅资料和研究官方案例 让本来就非常紧张的而宝贵的精力卷到了不关紧要的地方 而大多二三维的包 ope
  • apk文件结构解析一

    Apk文件用WINRAR压缩软件解压缩后 如下图所示 主要是三个文件 lib META INF res 和三个文件夹 AndroidManifest xml classes dex resources arsc 用工具展开可以看到如下所示的
  • Protobuf在java中的简单使用实例

    TTprotobuf是一种跨语言的数据转换协议 由google开源的 已支持大部份语言 在一般的数据交互过程中都是使用json xml等来做数据的转换 这其中涉及复杂的解析与序列化反序列化问题 如果在大量数据并发请求时 也会导致性能问题 p
  • Vue-过滤器

    Vue 过滤器 过滤器 过滤器 Filters 是vue 为开发者提供的功能 常用于文本的格式化 过滤器可以用在两个地方 插值表达式 和 v bind 属性绑定 过滤器应该被添加在JavaScript 表达式的尾部 由 管道符 进行调用 p
  • 图像增强之同态滤波python实现——20221204工作总结

    目录 参考 同态滤波python实现 代码 效果 参考 1 https wenku baidu com view 4eb598180a12a21614791711cc7931b765ce7ba9 html wkts 167005422236
  • IDEA 调试前端代码JS 及安装JetBrains IDE Support

    收藏从未间断 学习从未开始 不需要F12窗口 不需要JS代码写debugger断点 不需要写console log 输出 一 JetBrains IDE Support 1 必须使用谷歌浏览器 安装JetBrains IDE Support
  • go 进阶 sync相关: 一. sync.atomic​ 原子操作

    sync atomic 原子操作 一 sync atomic 基础 sync atomic Value 二 sync atomic 源码分析 1 ifaceWords 2 Store 添加 3 Load 获取 一 sync atomic 基