(資料圖)
前面幾篇文章介紹了Golang中互斥鎖、讀寫鎖、條件變量,雖然它們可以很好地協(xié)調(diào)對共享資源的訪問,但并不能保證原子操作。
原子操作原子操作是指一系列操作要么全部執(zhí)行成功,要么全部執(zhí)行失敗,不會有中間狀態(tài)。
鎖無法保證原子性是因為:
在鎖保護的臨界區(qū)代碼執(zhí)行期間,其他協(xié)程無法訪問該代碼段,但是它們可以訪問其他資源,可能會導(dǎo)致原子操作失??;鎖雖然能做到只讓一個goroutine執(zhí)行臨界區(qū)代碼,不被其他goroutine打擾,不過仍然可能被系統(tǒng)中斷(因為goroutine都是統(tǒng)一被runtime調(diào)度的,runtime會頻繁切換一個goroutine的運行狀態(tài))可以看出原子操作的粒度更細,它對單個變量的訪問進行了原子化保證,在操作完成之前會阻塞其他并發(fā)操作。能夠保證原子性執(zhí)行的只有原子操作,原子操作在執(zhí)行過程中是不允許被中斷的。在計算機底層,原子性是由CPU支持的,所以絕對有效。Golang中的原子操作是基于操作系統(tǒng)和CPU的,具體功能由標準庫中的sync/atomic包提供。
sync/atomicsync/atomic包提供的原子操作有Add、Load、Store、Swap和CompareAndSwap,這些函數(shù)支持的數(shù)據(jù)類型有int32、int64、uint32、uint64、uintptr和unsafe包中的Pointer,不過,沒有提供針對unsafe.Pointer的Add方法。具體方法如下:
AddInt32/AddInt64/AddUint32/AddUint64/AddUintptr: 原子地將指定的值加到一個地址中的值上。CompareAndSwapInt32/CompareAndSwapInt64/CompareAndSwapUint32/CompareAndSwapUint64/CompareAndSwapUintptr/CompareAndSwapPointer: 原子地比較一個指定類型地址中的值,如果該值和參數(shù)old匹配,就在那個地址處存儲參數(shù)new。SwapInt32/SwapInt64/SwapUint32/SwapUint64/SwapUintptr/SwapPointer: 原子地將值存儲在指定地址處,并返回此地址的舊值。LoadInt32/LoadInt64/LoadUint32/LoadUint64/LoadUintptr/LoadPointer: 原子地返回指定地址中的值。StoreInt32/StoreInt64/StoreUint32/StoreUint64/StoreUintptr/StorePointer: 原子地將指定值存儲到指定類型地址中。此外,sync/atomic包還提供一個名稱為Value的類型,可以被用來存儲任意類型的值,結(jié)構(gòu)如下:
type Value struct {v any}
使用方法和示例使用原子操作可以用于計算需要在多個goroutine之間共享的計數(shù)器。例如,計算在線用戶數(shù)量、任務(wù)完成情況等等。
package mainimport ("fmt""sync/atomic")func main() {var counter int64done := make(chan bool)for i := 0; i < 100; i++ {go func() {atomic.AddInt64(&counter, 1)done <- true}()}for i := 0; i < 100; i++ {<-done}fmt.Println(counter)}
首先聲明了一個int64類型的計數(shù)器變量counter,使用AddInt64原子操作對其進行遞增。通過使用AddInt64,確保了每個goroutine對其值的修改操作都能夠安全進行。
再看一個自旋鎖的示例:
package mainimport ("fmt""sync/atomic""time")func main() {sign := make(chan struct{}, 2)var num int32go func() {defer func() {sign <- struct{}{}}()for {// 定時增大num值time.Sleep(time.Millisecond * 500)newNum := atomic.AddInt32(&num, 2)fmt.Printf("num當前值為: %d\n", newNum)// 滿足條件后退出if newNum == 10 {break}}}()go func() {// 定時檢查num值,等于10則歸零defer func() {sign <- struct{}{}}()for {if atomic.CompareAndSwapInt32(&num, 10, 0) {fmt.Println("已將num歸零")break}time.Sleep(time.Millisecond * 500)}}()<-sign<-sign}
標簽: