Go語言并發(fā)編程 sync.Once
sync.Once
用于保證某個動作只被執(zhí)行一次,可用于單例模式中,比如初始化配置。我們知道init()函數(shù)也只會執(zhí)行一次,不過它是在main()
函數(shù)之前執(zhí)行,如果想要在代碼執(zhí)行過程中只運行某個動作一次,可以使用sync.Once
,下面來介紹一下它的使用方法。
先來看下面的代碼:
package main import ( "fmt" "sync" ) func main() { var num = 6 var once sync.Once add_one := func() { num = num + 1 } minus_one := func() { num = num - 1 } once.Do(add_one) fmt.Printf("The num: %d\n", num) once.Do(minus_one) fmt.Printf("The num: %d\n", num) }
執(zhí)行結(jié)果:
The num: 7
The num: 7
sync.Once
類型提供了一個Do
方法,Do方法只接受一個參數(shù),且參數(shù)類型必須是func()
,也就是沒有參數(shù)聲明和結(jié)果聲明的函數(shù)。
Do
方法只會執(zhí)行首次被調(diào)用時傳入的那個函數(shù),只執(zhí)行一次,也不會執(zhí)行其它函數(shù)。上面的例子中,即使傳入的函數(shù)不同,也只會執(zhí)行第一次傳入的那個函數(shù)。如果有多個只執(zhí)行一次的函數(shù),需要為每一個函數(shù)分配一個sync.Once
類型的值:
func main() { var num = 6 var once1 sync.Once var once2 sync.Once add_one := func() { num = num + 1 } minus_one := func() { num = num - 1 } once1.Do(add_one) fmt.Printf("The num: %d\n", num) once2.Do(minus_one) fmt.Printf("The num: %d\n", num) }
sync.Once
類型是一個結(jié)構(gòu)體類型,一個是名為done
的uint32
類型字段,還有一個互斥鎖m
。
type Once struct { done uint32 m Mutex }
done
字段的值只可能是0或者1,Do
方法首次調(diào)用完成后,done
的值就變?yōu)榱?。done的值使用四個字節(jié)的uint32
類型的原因是為了保證對它的操作是“原子操作”,通過調(diào)用atomic.LoadUint32
函數(shù)獲取它的值,如果為1,直接返回,不會執(zhí)行函數(shù)。
如果為0,Do方法會立即鎖定字段m,如果這里不加鎖,多個goroutine
同時執(zhí)行到Do方法時判斷都為0,則都會執(zhí)行函數(shù),所以Once
是并發(fā)安全的。
加鎖之后,會再次檢查done
字段的值,如果滿足條件,執(zhí)行傳入的函數(shù),并用原子操作函數(shù)atomic.StoreUint32
將done
的值設置為1。
下面是Once的源碼:
func (o *Once) Do(f func()) { if atomic.LoadUint32(&o.done) == 0 { o.doSlow(f) } } func (o *Once) doSlow(f func()) { o.m.Lock() defer o.m.Unlock() if o.done == 0 { defer atomic.StoreUint32(&o.done, 1) f() } }
源碼非常簡潔,和GoF 設計模式中的單例模式非常相似。
到此這篇關于Go語言并發(fā)編程 sync.Once的文章就介紹到這了,更多相關Go語言 sync.Once內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
基于Go實現(xiàn)TCP長連接上的請求數(shù)控制
在服務端開啟長連接的情況下,四層負載均衡轉(zhuǎn)發(fā)請求時,會出現(xiàn)服務端收到的請求qps不均勻的情況或是服務器無法接受到請求,因此需要服務端定期主動斷開一些長連接,所以本文給大家介紹了基于Go實現(xiàn)TCP長連接上的請求數(shù)控制,需要的朋友可以參考下2024-05-05