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