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流程:

  1. 判断要Store的值是否为空,如果为空直接panic
  2. 将Value和要存入的值都转换成efaceWords的指针类型
  3. 使用for循环,首先取出Value的typ
    1. 如果typ为空,表明这是第一次进行Store操作,runtime_procPin()是在运行时实现的方法,可以禁止其他协程抢占M,然后通过cas操作来更新typ的值,这里改为firstStoreInProgress,说明正在第一次Store的过程中
      1. 如果cas操作失败,说明已经有其他协程将typ的值改变了,那么解除禁止抢占,回到循环的最开始,重新取typ
      2. 如果cas成功,那么更新data值和typ,解除禁止抢占,这里先更新data的值再更新typ,是因为这里是由typ的值来确定是否更新成功的,如果先更新typ,有可能出现data没有存,但是typ已经存好的情况。
    2. 如果typ的值为firstStoreInProgress,说明其他协程在第一次Store的过程中,那么自旋等待。
    3. 走到这里,typ的值既不为nil也不为中间状态,那么直接与要存入的vlp的typ进行比较,如果不同那么panic,如果相同则进行存值

这里通过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

总结

  1. 从atomic.Value的实现可以看到,Value的更新并没有使用锁,而是通过原子操作和cas和自旋实现。
  2. 在进行原子修改结构体的时候,可以抓住一个字段的状态,来判断操作进行到了哪一步
posted @   每天提醒自己要学习  阅读(305)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示