atomic.Value
atomic.Value
在正式介绍atomic.Value
前, 先介绍一个基本知识, 任何Interface, 在Go中都会表示为Type +Data
package main
import (
"fmt"
"unsafe"
)
type A struct {
v int64
}
type eStruct struct {
typ unsafe.Pointer
data unsafe.Pointer
}
func getAnyAndPrint(v any) {
size := unsafe.Sizeof(v)
println("in getAnyAndPrint: ", size)
realData := (*eStruct)(unsafe.Pointer(&v))
println("this is Type Pointer", realData.typ)
println("this is int64 Pointer", realData.data)
i := (*int64)(realData.data)
println("this is int64 Value", *i)
}
func main() {
a := A{
v: 10,
}
size := unsafe.Sizeof(a)
fmt.Printf("Size of A: %d bytes\n", size)
getAnyAndPrint(&a)
}
以上是我自己构建的一个case, 想要向你表达两件事:
- 当使用
interface
时, 实际上是在用Type + Data, 这两者分别占用64 bit. - 可以通过一些hack手段实现直接对typ/data的修改.
请注意eStruct
的使用.
源码注释
package atomic
import (
"unsafe"
)
type Value struct {
v any // interface 64bit
}
type efaceWords struct {
typ unsafe.Pointer // interface Type
data unsafe.Pointer // interface Data
}
func (v *Value) Load() (val any) {
vp := (*efaceWords)(unsafe.Pointer(v)) // 获取的数据: Type + Data
typ := LoadPointer(&vp.typ) // 获取Type
if typ == nil || typ == unsafe.Pointer(&firstStoreInProgress) {
// 如果type 为nil, 表示此时刚刚初始化, 还没有调用Store
// 如果type 为&firstStoreInProgress, 表示调用了第一次Store, 但是没有执行完成.
// 这两者都会导致无法Load, 此时返回nil
return nil
}
data := LoadPointer(&vp.data) // 获取实际的Data
// 实际进行返回值的复制
vlp := (*efaceWords)(unsafe.Pointer(&val))
vlp.typ = typ
vlp.data = data
return
}
var firstStoreInProgress byte
func (v *Value) Store(val any) {
// 如果要写入的val是nil, 直接抛出
if val == nil {
panic("sync/atomic: store of nil value into Value")
}
vp := (*efaceWords)(unsafe.Pointer(v)) // 解析现在的存储的值
vlp := (*efaceWords)(unsafe.Pointer(&val)) // 解析要写入的数据
for {
typ := LoadPointer(&vp.typ) // 原子性的获取当前的指定值
if typ == nil { // 如果为nil, 表示这是第一次赋值
runtime_procPin()
// 尝试占用赋值, 通过修改Type字段实现
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(&firstStoreInProgress)) {
runtime_procUnpin()
continue
}
// 写入Data
StorePointer(&vp.data, vlp.data)
// 写入Type: 真实的Type
StorePointer(&vp.typ, vlp.typ)
runtime_procUnpin()
return
}
// 如果当前的类型不为nil, 但是是firstStoreInProgress, 表示此时有其他goroutine在处理, 本次循环忽略
if typ == unsafe.Pointer(&firstStoreInProgress) {
continue
}
// 第一次赋值完成, 检查类型是否变更了, 变更了直接抛错
if typ != vlp.typ {
panic("sync/atomic: store of inconsistently typed value into Value")
}
// 原子性的更新Data
StorePointer(&vp.data, vlp.data)
return
}
}
解释
其实这不是一段难懂的代码
唯一需要注意的就是efaceWords
的使用.
其他的处理就很简单了, 确认Type
, 处理Type
的初始化, 通过调用StorePointer
实现更新Interface.Data