神奇的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
}
相关推荐
PFinal社区_南丞5 分钟前
Go 1.26 go fix 详解:18 个分析器一键现代化代码
后端
beiju8 分钟前
Agent Loop:AI Agent 的最小实现结构
后端·agent
野生技术架构师9 分钟前
Spring Boot 4 与 Spring Framework 7 全面解析:新特性、升级要点与实战指南
spring boot·后端·spring
Java水解9 分钟前
阿里国际Java社招面经分享(附赠阿里Java面试题)
java·后端·面试
Nyarlathotep011317 分钟前
CyclicBarrier基础和原理
java·后端
菜鸟程序员专写BUG32 分钟前
SpringBoot跨域报错全集|CORS、OPTIONS预检、无Access-Control报错全解决
spring boot·后端·状态模式
无籽西瓜a1 小时前
【西瓜带你学设计模式 | 第五期 - 建造者模式】建造者模式 —— 产品构建实现、优缺点与适用场景及模式区别
java·后端·设计模式·软件工程·建造者模式
小江的记录本2 小时前
【Spring注解】Spring生态常见注解——面试高频考点总结
java·spring boot·后端·spring·面试·架构·mvc
程序员cxuan2 小时前
来了来了,Claude Code 全架构解析 !!!
人工智能·后端·claude
艾莉丝努力练剑2 小时前
【Linux信号】Linux进程信号(下):可重入函数、Volatile关键字、SIGCHLD信号
linux·运维·服务器·c++·人工智能·后端·学习