核心用途: 原子地设置和读取变量
- 不能用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种情况处理:
- (第一次写入) 使用
CAS
操作,先尝试将typ
设置为unsafe.Pointer(&firstStoreInProgress)
中间状态。如果失败,则说明其他协程抢先完成了赋值操作,继续for循环。如果设置成功,那证明当前协程抢到了这个"乐观锁",则先写data
字段,再设置typ
字段 - (第一次写入未完成) 继续循环,"忙等"直到第一次写入完成
- (第一次写入已完成) 检查上一次写入的类型与这一次要写入的类型是否一致,如果不一致则抛出异常。反之,则直接把这一次要写入的值写入到
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 方法
- 如果当前的
typ
是 nil 或者typ == unsafe.Pointer(&firstStoreInProgress)
,证明第一次写入还没有开始,或者还没完成,那就直接返回 nil - 反之,基于当前的
typ
和data
构造出一个新的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
}