参考:
https://www.jianshu.com/p/6ca885ede2a8(sync/atomaic原子操作)
协程并发问题
在goroutine中访问外部的变量并不安全,我们先看看下面这个例子,我们执行一次计数,使用
sync.WaitGroup
包保证我们创建的1000个goroutine全部执行完毕后再输出n
,运行程序看看结果如何:func no_atomic() { var n int32 var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { n++ wg.Done() }() } wg.Wait() fmt.Println(atomic.LoadInt32(&n)) // 978 }
电脑多核的情况下,不限制cpu执行上面代码,每次执行的结果不是1000,而是小于等于一千数值波动。为什么会出现这种情况呢?上面我们通过for循环里创建1000个goroutine,每个goroutine都将n加上1,加入有其中两个goroutine如下执行步骤:
- goroutine1读取变量n 值为800
- goroutine2读取变量n 值为800
- goroutine1执行n+1,n变为801
- goroutine2执行n+1,n变为801
- 两次goroutine执行完毕
- 结果n少加了一次1,所以最终的结果就比预期少1
使用sync/atomic
使用sync/atomic原子操作解决上述并发安全问题,代码如下:
func _atomic() { var n int32 var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { atomic.AddInt32(&n, 1) wg.Done() }() } wg.Wait() fmt.Println(atomic.LoadInt32(&n)) // 1000 }
使用
atomic.AddInt32
进行加操作,最终输出预期的结果, 其实``atomic`包中的方法在执行完毕之前不会被其他的任务或者事件中断,该操作是并发安全,在向此地址写入或者读取值时原子性的操作,如果其他goroutine尝试读取或写入会被阻塞,知道此次原子操作完成。sync/atomic的原子操作
主要有五大类:
Load:该类方法主要负责从相应的内存地址中获取对应的值
Store:该类主要负责将对应的值保存在相应的内存地址中
Add:该类方法可以理解是Load和Store的结合,也就是先Load然后Add
Swap:该类方法可以理解为先Load,在Store新值,然后返回旧值
CompareAndSwap:该类方法可以这样理解:先比较旧数据和地址中保存数据的值,如果相同的话,执行Swap,把新的数值保存在地址中,返回true,如果不同,那么直接返回false
常用方法:
func AddT(addr *T, delta T)(new T) //计数 func LoadT(addr *T) (val T) //获取值 func StoreT(addr *T, val T) //设置值 func SwapT(addr *T, new T) (old T) //设置值然后返回旧值 func CompareAndSwapT(addr *T, old, new T) (swapped bool)
具体
// 下面是in32的 func AddInt32(addr *int32, delta int32)(new int32) func LoadInt32(addr *int32) (val int32) func StoreInt32(addr *int32, val int32) func SwapInt32(addr *int32, new int32) (old int32) func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) // 为(安全)指针类型提供以下四个原子函数: // 并没有AddPointer函数,因为go的指针不支持算术运算 func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) func SwapPointer(addr *unsafe.Pointer, new T) (old unsafe.Pointer) func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)