go语言atomic.Value
go语言原子操作atomic.Value
数据结构
// 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
}
any是interface{}的别名,aotomic.Value
维护的是一个interface{},提供一些原子的方法来更新这个interface{}
atomic.Value
中Stroe的类型要与第一次Store的是同一个类型,并且Store被调用以后不能被复制,在第一次被使用后也是不能被复制的。
// efaceWords is interface{} internal representation.
type efaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
注释说明,efaceWords结构体是interface{}的内部表示,后面在使用interface{}的时候,会转化成efaceWords进行操作。
接口的底层数据结构
type iface struct {
tab *itab
data unsafe.Pointer
}
type eface struct {
_type *_type
data unsafe.Pointer
}
Store
// Store sets the value of the Value v to val.
// 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")
}
vp := (*efaceWords)(unsafe.Pointer(v))
vlp := (*efaceWords)(unsafe.Pointer(&val))
for {
typ := LoadPointer(&vp.typ)
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()
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
}
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
}
// First store completed. Check type and overwrite data.
if typ != vlp.typ {
panic("sync/atomic: store of inconsistently typed value into Value")
}
StorePointer(&vp.data, vlp.data)
return
}
}
Store流程:
- 判断要Store的值是否为空,如果为空直接panic
- 将Value和要存入的值都转换成efaceWords的指针类型
- 使用for循环,首先取出Value的typ
- 如果typ为空,表明这是第一次进行Store操作,runtime_procPin()是在运行时实现的方法,可以禁止其他协程抢占M,然后通过cas操作来更新typ的值,这里改为
firstStoreInProgress
,说明正在第一次Store的过程中- 如果cas操作失败,说明已经有其他协程将typ的值改变了,那么解除禁止抢占,回到循环的最开始,重新取typ
- 如果cas成功,那么更新data值和typ,解除禁止抢占,这里先更新data的值再更新typ,是因为这里是由typ的值来确定是否更新成功的,如果先更新typ,有可能出现data没有存,但是typ已经存好的情况。
- 如果typ的值为
firstStoreInProgress
,说明其他协程在第一次Store的过程中,那么自旋等待。 - 走到这里,typ的值既不为nil也不为中间状态,那么直接与要存入的vlp的typ进行比较,如果不同那么panic,如果相同则进行存值
- 如果typ为空,表明这是第一次进行Store操作,runtime_procPin()是在运行时实现的方法,可以禁止其他协程抢占M,然后通过cas操作来更新typ的值,这里改为
这里通过typ的值来表示程序进行Store的完成情况。
TEXT runtime∕internal∕atomic·Cas(SB),NOSPLIT,$0-17
MOVQ ptr+0(FP), BX
MOVL old+8(FP), AX
MOVL new+12(FP), CX
LOCK
CMPXCHGL CX, 0(BX)
SETEQ ret+16(FP)
RET
atomic包中的cas通过LOCK指令来保证原子性,对于P6之前的处理器,LOCK 指令会总是锁总线,但是 P6 之后可能会执行“缓存锁定”,如果被锁定的内存区域被缓存在了处理器中,这个时候会通过缓存一致性来保证操作的原子性
Load
// 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 := (*efaceWords)(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 := (*efaceWords)(unsafe.Pointer(&val))
vlp.typ = typ
vlp.data = data
return
}
Load返回最近一次存的值,如果没有Store过或者typ为firstStoreInProgress
的话,返回nil
总结
- 从atomic.Value的实现可以看到,Value的更新并没有使用锁,而是通过原子操作和cas和自旋实现。
- 在进行原子修改结构体的时候,可以抓住一个字段的状态,来判断操作进行到了哪一步
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具