返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

atomic

发布于 2024-10-12 19:15:54 字数 4517 浏览 0 评论 0 收藏 0

原子操作 意为 “不可被中断的一个或一系列操作”。

原子操作一旦开始,直到结束,中间不会有任何上下文切换(context switch)。处理器通过总线锁定和缓存锁定确保原子性,让多线程不能同一时间访问相同资源。

原子性不可能由软件单独保证 —— 必须有硬件支持,因此和架构相关。在 x86 平台上,CPU 提供在指令执行期间对总线加锁手段。如果在汇编指令前加上 LOCK 前缀,其机器代码就使 CPU 在执行这条指令时锁住总线(或缓存锁定)。如此,同一总线上别的 CPU 暂时不能通过总线访问内存,保证这条指令在多处理器环境中的原子性。

// runtime/internal/atomic/atomic_amd64.s

// bool	·Cas64(uint64 *val, uint64 old, uint64 new)
// Atomically:
//	if(*val == old){
//		*val = new;
//		return 1;
//	} else {
//		return 0;
//	}
TEXT ·Cas64(SB), NOSPLIT, $0-25
    MOVQ	ptr+0(FP), BX
    MOVQ	old+8(FP), AX
    MOVQ	new+16(FP), CX
    LOCK
    CMPXCHGQ	CX, 0(BX)
    SETEQ	ret+24(FP)
	RET

Value

算是对原子操作的一种包装,不再局限于几种有限的数字类型。

多次存储的数据必须是同一类型。
需要考虑接口参数的额外开销。

// sync/atomic/value.go

type Value struct {
    
    // 用指针转换为:ifaceWords。
    // 当独立字段用:typ, data。
    
    v  any 
}

type ifaceWords struct {
	typ   unsafe.Pointer
	data  unsafe.Pointer
}

标记第一次写,存入类型信息供后续检查。

// 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) {
    
    // 不能存储 nil。
	if val == nil {
		panic("store of nil value into Value")
	}
    
    // 将 v、&val 转换为 ifaceWords,以读取接口内部信息。
	vp := (*ifaceWords)(unsafe.Pointer(v))
	vlp := (*ifaceWords)(unsafe.Pointer(&val))
    
	for {        
		typ := LoadPointer(&vp.typ)
        
        // 当前 Value 还没有存储类型信息。
		if typ == nil {
			runtime_procPin()
            
            // 第一次存储。写入正在存储标记,避免被其他人修改。
			if !CompareAndSwapPointer(&vp.typ, nil, 
                       unsafe.Pointer(&firstStoreInProgress)) {
                
				runtime_procUnpin()
				continue
			}
            
			// 将 val.typ & .data 写入 Value。
			StorePointer(&vp.data, vlp.data)
			StorePointer(&vp.typ, vlp.typ)
            
			runtime_procUnpin()
			return
		}
        
        // 有人正在进行第一次存储。
		if typ == unsafe.Pointer(&firstStoreInProgress) {
			// First store in progress. Wait.
			continue
		}
        
		// 检查类型,不一致导致 panic!
		if typ != vlp.typ {
			panic("store of inconsistently typed value into Value")
		}
        
        // 修改 Value.data。
		StorePointer(&vp.data, vlp.data)
        
		return
	}
}

var firstStoreInProgress byte

载入操作用存储的数据,重新组装一个接口对象。

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)
    
    // 对返回值 { .typ, .data } 分别赋值,
    // 组成完整接口对象。
    
	vlp := (*ifaceWords)(unsafe.Pointer(&val))
	vlp.typ = typ
	vlp.data = data
    
	return
}

至于 CAS,无非是类型和旧值都要一致。

func (v *Value) CompareAndSwap(old, new any) (swapped bool) {
    
	if new == nil {
		panic("compare and swap of nil value into Value")
	}
    
	vp := (*ifaceWords)(unsafe.Pointer(v))     // Value
	np := (*ifaceWords)(unsafe.Pointer(&new))  
	op := (*ifaceWords)(unsafe.Pointer(&old))
    
    // 新旧值类型必须一致。
	if op.typ != nil && np.typ != op.typ {
		panic("compare and swap of inconsistently typed values")
	}
    
	for {
        
        // 当前 Value 存储的类型。
		typ := LoadPointer(&vp.typ)
        
		if typ == nil {
            
            // 这个 old 肯定不符。
			if old != nil {
				return false
			}
            
            // 首次存储(new)。
			runtime_procPin()
			if !CompareAndSwapPointer(&vp.typ, nil, 
                       unsafe.Pointer(&firstStoreInProgress)) {
				runtime_procUnpin()
				continue
			}
            
			StorePointer(&vp.data, np.data)
			StorePointer(&vp.typ, np.typ)
            
			runtime_procUnpin()
			return true
		}
        
		if typ == unsafe.Pointer(&firstStoreInProgress) {
			// First store in progress. Wait.
			continue
		}
        
        // 新值类型和已存储类型是否相同。
		if typ != np.typ {
			panic("compare and swap of inconsistently typed value into Value")
		}
        
        // 将当前存储值重新组成接口对象。
		data := LoadPointer(&vp.data)
		var i any
		(*ifaceWords)(unsafe.Pointer(&i)).typ = typ
		(*ifaceWords)(unsafe.Pointer(&i)).data = data

        // 再与旧值(old)进行比较。
		if i != old {
			return false
		}
        
        // CAS 写入新值。
		return CompareAndSwapPointer(&vp.data, data, np.data)
	}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文