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

