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()


参考文章