神奇的atomic.Value

核心用途: 原子地设置和读取变量

  • 不能用atomic.Value原子值存储nil
  • 第一次向原子值存储值,决定了它今后能且只能存储该类型的值
go 复制代码
// A Value provides an atomic load and store of a consistently typed value.
// The zero value for a Value returns nil from Load.
// Once Store has been called, a Value must not be copied.
//
// A Value must not be copied after first use.
type Value struct {
    v any
}

// ifaceWords is interface{} internal representation.
type ifaceWords struct {
    // 指向类型
    typ  unsafe.Pointer
    // 指向数据
    data unsafe.Pointer
}

Store 方法

  • 通过unsafe.Pointer现有的要写入的 值分别转成ifaceWords类型(接口的实现参考必知必会系列-interface),得到这两个interface{}的原始类型(typ)和真正的值(data)
  • 通过LoadPointer这个原子操作拿到当前Value中存储的类型。下面根据这个类型的不同,分3种情况处理:
  1. (第一次写入) 使用CAS操作,先尝试将typ设置为unsafe.Pointer(&firstStoreInProgress)中间状态。如果失败,则说明其他协程抢先完成了赋值操作,继续for循环。如果设置成功,那证明当前协程抢到了这个"乐观锁",则先写data字段,再设置typ字段
  2. (第一次写入未完成) 继续循环,"忙等"直到第一次写入完成
  3. (第一次写入已完成) 检查上一次写入的类型与这一次要写入的类型是否一致,如果不一致则抛出异常。反之,则直接把这一次要写入的值写入到data字段
go 复制代码
var firstStoreInProgress byte

// Store sets the value of the Value to x.
// All calls to Store for a given Value must use values of the same concrete type.
// Store of an inconsistent type panics, as does Store(nil).
func (v *Value) Store(val any) {
    if val == nil {
        panic("sync/atomic: store of nil value into Value")
    }
    // 通过Unsafe转换为 interface的内部实现结构体
    vp := (*ifaceWords)(unsafe.Pointer(v))
    vlp := (*ifaceWords)(unsafe.Pointer(&val))
    for {
        typ := LoadPointer(&vp.typ)
        // tpe==nil 表示该字段第一次set
        if typ == nil {
            // Attempt to start first store.
            // Disable preemption so that other goroutines can use
            // active spin wait to wait for completion.
            runtime_procPin() // 关闭协程的抢占
            // 通过CAS 原子指令设置一个中间状态
            if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) {
                runtime_procUnpin()
                continue
            }
            // Complete first store.
            StorePointer(&vp.data, vlp.data)
            StorePointer(&vp.typ, vlp.typ)
            runtime_procUnpin()
            return
        }
        // 如果第一次set还没有完成,继续等待
        if typ == unsafe.Pointer(&firstStoreInProgress) {
            // First store in progress. Wait.
            // Since we disable preemption around the first store,
            // we can wait with active spinning.
            continue
        }
        // 第一次设置完成,便只需要比较类型,然后原子设置data字段便可
        if typ != vlp.typ {
            panic("sync/atomic: store of inconsistently typed value into Value")
        }
        StorePointer(&vp.data, vlp.data)
        return
    }
}

Load 方法

  1. 如果当前的typ是 nil 或者typ == unsafe.Pointer(&firstStoreInProgress),证明第一次写入还没有开始,或者还没完成,那就直接返回 nil
  2. 反之,基于当前的typdata构造出一个新的interface{}返回出去
go 复制代码
// Load returns the value set by the most recent Store.
// It returns nil if there has been no call to Store for this Value.
func (v *Value) Load() (val any) {
    vp := (*ifaceWords)(unsafe.Pointer(v)) 
    typ := LoadPointer(&vp.typ)
    if typ == nil || typ == unsafe.Pointer(&firstStoreInProgress) {
        // First store not yet completed.
        return nil
    }
    data := LoadPointer(&vp.data)
    vlp := (*ifaceWords)(unsafe.Pointer(&val))
    vlp.typ = typ
    vlp.data = data
    return
}
相关推荐
李慕婉学姐1 小时前
【开题答辩过程】以《基于Android的出租车运行监测系统设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·后端·vue
m0_740043731 小时前
SpringBoot05-配置文件-热加载/日志框架slf4j/接口文档工具Swagger/Knife4j
java·spring boot·后端·log4j
招风的黑耳2 小时前
我用SpringBoot撸了一个智慧水务监控平台
java·spring boot·后端
Miss_Chenzr2 小时前
Springboot优卖电商系统s7zmj(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
期待のcode2 小时前
Springboot核心构建插件
java·spring boot·后端
2501_921649492 小时前
如何获取美股实时行情:Python 量化交易指南
开发语言·后端·python·websocket·金融
serendipity_hky3 小时前
【SpringCloud | 第5篇】Seata分布式事务
分布式·后端·spring·spring cloud·seata·openfeign
五阿哥永琪3 小时前
Spring Boot 中自定义线程池的正确使用姿势:定义、注入与最佳实践
spring boot·后端·python
Victor3564 小时前
Netty(16)Netty的零拷贝机制是什么?它如何提高性能?
后端
Victor3564 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
后端