sync/atomic
Mutexlock-free
数据类型
- int32
- int64
- uint32
- uint64
- uintptr
- unsafe.Pointer
操作类型
*addr += delta
return *addr
if *addr == old {
*addr = new
return true
}
return false
return *addr
*addr = val
old = *addr
*addr = new
return old
什么操作叫做原子操作?
一个或者多个操作在CPU执行过程中不被中断的特性,称为原子性(atomicity)。这些操作对外表现成一个不可分割的整体,他们要么都执行,要么都不执行,外界不会看到他们只执行到一半的状态。而在现实世界中,CPU不可能不中断的执行一系列操作,但如果我们在执行多个操作时,能让他们的中间状态对外不可见,那我们就可以宣城他们拥有了“不可分割”的原子性。
在Go中,一条普通的赋值语句其实不是一个原子操作。列如,在32位机器上写int64类型的变量就会有中间状态,因为他会被拆成两次写操作(MOV)——写低32位和写高32位。
用锁行不行?
atomic.Value
值类型操作
- 如果一个线程刚写完低32位,还没来得及写高32位时,另一个线程读取了这个变量,那得到的就是一个毫无逻辑的中间变量,会导致程序出现诡异的bug。
//在被操作值被频繁变更的情况下,CAS操作并不那么容易成功
//利用for循环以进行多次尝试
var value int32
func addValue1(delta int32){
for{
//在进行读取value的操作的过程中,其他对此值的读写操作是可以被同时进行的
//那么这个读操作很可能会读取到一个只被修改了一半的数据
v := value
if atomic.CompareAndSwapInt32(&value, v, v + delta){
break
}
}
}
- 用Load函数防止只读取一半有效数据的发生
func addValue2(delta int32){
for{
//使用载入
v := atomic.LoadInt32(&value)
if atomic.CompareAndSwapInt32(&value, v, v + delta){
//在函数的结果值为true时,退出循环
break
}
}
}
struct类型操作
atomic.Valueunsafe.Pointer
// 使用示例
type Config struct {
Addr string
Port string
}
func (c Config) String() string {
return c.Addr + ":" + c.Port
}
func loadConfig() Config {
// do something
return Config{}
}
func automicValue() {
var config atomic.Value
wg := sync.WaitGroup{}
go func() {
for {
time.Sleep(time.Millisecond)
config.Store(loadConfig())
}
}()
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
c := config.Load().(Config)
fmt.Println(c)
wg.Done()
}()
}
wg.Wait()
}
atomic.Value 设计与实现
atomicatomic.Valueatomic.Valueatomic
runtime.GOMAXPROCS
//atomic.Value源码
type Value struct {
v interface{} // 所以可以存储任何类型的数据
}
// 空 interface{} 的内部表示格式,作用是将interface{}类型分解,得到其中两个字段
type ifaceWords struct {
typ unsafe.Pointer
data unsafe.Pointer
}
// 取数据就是正常走流程
func (v *Value) Load() (x interface{}) {
vp := (*ifaceWords)(unsafe.Pointer(v))
typ := LoadPointer(&vp.typ)
if typ == nil || uintptr(typ) == ^uintptr(0) {
// 第一次还没写入
return nil
}
// 构造新的interface{}返回出去
data := LoadPointer(&vp.data)
xp := (*ifaceWords)(unsafe.Pointer(&x))
xp.typ = typ
xp.data = data
return
}
// 写数据(如何保证数据完整性)
func (v *Value) Store(x interface{}) {
if x == nil {
panic("sync/atomic: store of nil value into Value")
}
// 绕过 Go 语言类型系统的检查,与任意的指针类型互相转换
vp := (*ifaceWords)(unsafe.Pointer(v)) // 旧值
xp := (*ifaceWords)(unsafe.Pointer(&x)) // 新值
for { // 配合CompareAndSwap达到乐观锁的功效
typ := LoadPointer(&vp.typ)
if typ == nil { // 第一次写入
runtime_procPin() // 禁止抢占
if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) {
runtime_procUnpin() // 没有抢到锁,说明已经有别的线程抢先完成赋值,重新进入循环
continue
}
// 首次赋值
StorePointer(&vp.data, xp.data)
StorePointer(&vp.typ, xp.typ)
runtime_procUnpin() // 写入成功,解除占用状态
return
}
if uintptr(typ) == ^uintptr(0) {
// 第一次写入还未完成,继续等待
continue
}
// 两次需要写入相同类型
if typ != xp.typ {
panic("sync/atomic: store of inconsistently typed value into Value")
}
StorePointer(&vp.data, xp.data)
return
}
}
// 禁止抢占,标记当前G在M上不会被抢占,并返回当前所在P的ID。
func runtime_procPin()
// 解除G的禁止抢占状态,之后G可被抢占。
func runtime_procUnpin()