谈一谈原子操作
Go语言最初的sync/atomic支持的数据类型就是几种基本的数据类型,直到Go 1.4版本之后,Go语言新增了Value类型,使得atomic可以支持任意类型数据的读取和存储
提到原子操作就不得不提到一个东西 - 锁,这个在我的历史文章【go源码阅读笔记】 - Sync.Mutex中有提及,感兴趣的读者可以先移步阅读,那么原子操作和锁的区别究竟是什么,首先锁是操作系统层面的,而原子操作是底层硬件提供的支持,在高并发的场景下,原子操作由于可以做到lock free,所以性能会比加锁要好很多
源码解析
在看源代码之前,我们首先需要了解在atomic中结构体的定义
// 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.
// Value就是存储和读取的结构体
type Value struct {
v interface{}
}
// ifaceWords is interface{} internal representation.
// 它的功能就是拆解Value结构题中的v,将它拆解为typ以及data
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
之后我们来看有关于atomic的两个操作,Load和Store
存储(Store)
// 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(x interface{}) {
// 如果存储元素为空,则直接抛出panic
if x == nil {
panic("sync/atomic: store of nil value into Value")
}
vp := (*ifaceWords)(unsafe.Pointer(v)) // 通过ifaceWords将旧的值进行分解
xp := (*ifaceWords)(unsafe.Pointer(&x)) // 通过ifaceWords将新的值进行分解
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; and so that
// GC does not see the fake type accidentally.
// 当前的这个goroutine优先占有CPU,进行下面的操作
runtime_procPin()
// 这里有一个CAS操作,也就是多个goroutine进行竞争,如果一直没有赋值成功,则当前goroutine
// 解除CPU的控制权,再与其它的goroutine一起竞争
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
runtime_procUnpin()
continue
}
// Complete first store.
// 对于类型和数据进行第一次存储
StorePointer(&vp.data, xp.data)
StorePointer(&vp.typ, xp.typ)
// 解除CPU的控制权
runtime_procUnpin()
return
}
// 如果当前类型还处于中间态,则说明还没有赋值完成,继续循环
if uintptr(typ) == ^uintptr(0) {
// 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.
// 如果说不是第一次存储,那么当类型与新的要存储的类型不匹配,抛出panic
if typ != xp.typ {
panic("sync/atomic: store of inconsistently typed value into Value")
}
// 类型一致,则直接修改data的值
StorePointer(&vp.data, xp.data)
return
}
}
读取(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() (x interface{}) {
// 获取当前存储元素的类型,如果说类型为空或者类型还处于中间态,则返回空
vp := (*ifaceWords)(unsafe.Pointer(v))
typ := LoadPointer(&vp.typ)
if typ == nil || uintptr(typ) == ^uintptr(0) {
// First store not yet completed.
return nil
}
// 获取data,并赋值给x
data := LoadPointer(&vp.data)
xp := (*ifaceWords)(unsafe.Pointer(&x))
xp.typ = typ
xp.data = data
return
}