goland sync/atomic原子操作
Jeff的技术栈 人气:01.go已经提供了锁,为什么还需要atomic原子操作?
1.加锁代价比较高,耗时多,需要上下文切换。加锁解锁在代码层实现,而代码是运行在用户态空间中,对底层进行操作时需要从用户态空间切换到内核空间,再由内核操作底层资源。耗时多
2.原子操作在用户态可以完成,性能比互斥锁高。原子操作在cpu层面支持的,cpu可以直接操作底层资源
3.原子操作需求步骤简单,无需加锁解锁步骤
2.atomic原子操作为什么比mutex快?
1.原子操作快,是因为依赖于cpu指令,而不是依赖外部锁。不会额外的上下文切换
2.原子操作能够保证执行期间是连续且不会被中断(变量不会被其他修改,mutex可能存在被其他修改的情况)
3.CAS
CAS是cpu硬件同步原语,是Compare And Swap的缩写(比较并交换),原子操作中CAS,再sync/atomic包中,全部以ComparAndSwap开头的函数名都是CAS操作
go中CAS操作,是借用CPU提供的原子性指令来实现。CAS操作修改共享变量时,不需要对共享变量加锁,而是通过类似乐观锁的方式进行检查,本质还是不断的占用CPU资源换取加锁带来的开销(如上下文切换时间开销)。
原子操作优势:
可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作。这可以大大的减少同步对程序性能的损耗。
原子操作劣势:
在被操作值被频繁的变更的情况下,CAS操作并不那么容易成功。因为需要对ild值进行匹配,只有匹配成功了才进行下一步的修改。
当前atmomic包有以下几种原子操作:
Add,ComparAndSwap,Load,Store,Swap
4.互斥锁与原子操作区别
互斥锁目的:互斥锁是用来保护一段逻辑的,保证并发安全。(比如操作数据库保护)
原子操作目的:原子操作作用于一个变量的更新保护,保证并发安全(比如操作数据库不能原子操作)
mutex底层实现:mutex由操作系统的调度器实现
原子操作底层实现:由底层硬件指令直接提供支持,这些指令在执行过程中不允许中断,因此原子操作可以在无锁的情况下保证并发安全,性能随cpu的数量增多而线性扩展。
5.原子操作方法
5.1 atomic.AddInt32--增减
增减,操作方法的命名方式为AddXXX,保证对操作数进行原子的增减,支持的类型为int32、int64、uint32、uint64、uintptr,使用时以AddXXX就是对应的操作方法。
//加 func demo() { var count int32 = 0 atomic.AddInt32(&count, 10) fmt.Println(count) //10 } //减 func demo() { var count int32 = 0 atomic.AddInt32(&count, -10) fmt.Println(count) //-10 }
锁和原子操作对比:
//Mutex锁 func demo1() { sta := time.Now().Unix() count := 0 mux := sync.Mutex{} wg := sync.WaitGroup{} for i := 0; i < 10000; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 10000; j++ { mux.Lock() count++ mux.Unlock() } }() } wg.Wait() fmt.Println(count) //100000000 fmt.Println(time.Now().Unix() - sta) //10秒 } //atomic原子操作:快2倍不止 func demo2() { sta := time.Now().Unix() wg := sync.WaitGroup{} var count int32 = 0 for i := 0; i < 10000; i++ { wg.Add(1) go func() { defer wg.Done() for j := 0; j < 10000; j++ { atomic.AddInt32(&count, 1) } }() } wg.Wait() fmt.Println(count) //100000000 fmt.Println(time.Now().Unix() - sta) //4秒 }
5.2 CAS-atomic.CompareAndSwapInt32--比较并替换
CompareAndSwap:比较并替换,类似乐观锁,先比较下old值与当前值是否一致,一致则把new的值替换
操作方法的命名方式为CompareAndSwapXXX
//true func demo3() { var count int32 = 0 boo := atomic.CompareAndSwapInt32(&count, 0, 100) fmt.Println(count) //100 fmt.Println(boo) //true } //false func demo3() { var count int32 = 0 boo := atomic.CompareAndSwapInt32(&count, 10, 100) fmt.Println(count) //0 fmt.Println(boo) //false }
5.3 atomic.StoreInt32--写操作
func demo3() { var count int32 = 0 atomic.StoreInt32(&count, 666) fmt.Println(count) //666 }
5.4 atomic.LoadInt32--读操作
func demo3() { var count int32 = 0 atomic.StoreInt32(&count, 666) val := atomic.LoadInt32(&count) fmt.Println(val) //666 }
5.5 atomic.SwapInt32--直接交换
atomic.SwapInt32:直接交换,并返回交换前的值 func demo3() { var count int32 = 0 old := atomic.SwapInt32(&count, 100) fmt.Println(old) //0 fmt.Println(count) //100 }
加载全部内容