前言:为了保证并发安全,go语言中可以使用原子操作。其执行过程不能被中断,这也就保证了同一时刻一个线程的执行不会被其他线程中断,也保证了多线程下数据操作的一致性。
1.sync/atomic包
2.Load和Store并发不安全
1.sync/atomic包
在atomic包中对几种基础类型提供了原子操作,包括int32,int64,uint32,uint64,uintptr,unsafe.Pointer。
对于每一种类型,提供了五类原子操作分别是
- Add, 增加和减少
- CompareAndSwap, 比较并交换
- Swap, 交换
- Load , 读取
- Store, 存储
2.Load和Store并发不安全
Load和Store操作对应与变量的原子性读写,许多变量的读写无法在一个时钟周期内完成,而此时执行可能会被调度到其他线程,无法保证并发安全。
⚠️Load 只保证读取的不是正在写入的值,Store只保证写入是原子操作。
所以在使用的时候要注意。
下面简单示例:
package main
import (
"sync"
"sync/atomic"
"fmt"
)
var (
wg sync.WaitGroup
mu sync.Mutex
num int32
)
func wrap(callback func()) {
wg.Add(1)
go func() {
callback()
wg.Done()
}()
}
// go build -race
func main() {
// 1099 、892, 并发不安全
//wrap(incNumAtomic)
//wrap(incNumAtomic)
// 1200, 并发安全
wrap(incNumAtomic2)
wrap(incNumAtomic2)
wg.Wait()
fmt.Printf("num=%d\n", num)
}
func incNumAtomic() {
for i := 0; i < 600; i++ {
// atomic.Load*系列函数只能保证读取的不是正在写入的值(比如只被修改了一半的数据)
// 同样的atomic.Store* 只保证写入是原子操作(保证写入操作的完整性)
val := atomic.LoadInt32(&num)
atomic.StoreInt32(&num, val+1)
}
}
func incNumAtomic2() {
for i := 0; i < 600; i++ {
atomic.AddInt32(&num, 1)
}
}
总结
点击👇查看源码,哈哈哈...