深度剖析Golang sync.Once源碼

目錄

  • 什么是sync.Once
  • 如何使用sync.Once
  • 源碼分析

什么是sync.Once

Once 可以用來執(zhí)行且僅僅執(zhí)行一次動作,常常用于單例對象的初始化場景。

Once 常常用來初始化單例資源,或者并發(fā)訪問只需初始化一次的共享資源醒串,或者在測試的時候初始化一次測試資源城侧。

sync.Once 只暴露了一個方法 Do,你可以多次調(diào)用 Do 方法劫拢,但是只有第一次調(diào)用 Do 方法時 f 參數(shù)才會執(zhí)行,這里的 f 是一個無參數(shù)無返回值的函數(shù)强胰。

如何使用sync.Once

就拿我負責的一個項目來說舱沧,因為項目的配置是掛在第三方平臺上,所以在項目啟動時需要獲取資源配置偶洋,因為需要一個方法來保證配置僅此只獲取一次熟吏,因此,我們考慮使用 sync.Once 來獲取資源玄窝。這樣的話牵寺,可以防止在其他地方調(diào)用獲取資源方法,該方法僅此執(zhí)行一次恩脂。

下面我簡單寫個Demo來演示一個sync.Once如何使用

package main
import (
   "fmt"
   "sync"
)
var once sync.Once
var con string
func main() {
   once.Do(func() {
      con = "hello Test once.Do"
   })
   fmt.Println(con)
}

代碼說明:

代碼的話比較簡單帽氓,就是通過調(diào)用Do方法,采用閉包方式东亦,將字符串("hello Test once.Do")賦值給con杏节,進而打印出值,這就是 sync.Once 的使用典阵,比較容易上手奋渔。

但我們用一個方法或者框架時,如果不對其了如指掌壮啊,總有點不太靠譜嫉鲸,感覺心里不踏實。為此歹啼,我們來聊一聊 sync.Once 的源碼實現(xiàn)玄渗,讓他無處可遁。

源碼分析

接下來分析 sync.Do 究竟是如何實現(xiàn)的狸眼,它存儲在包sync下 once.go 文件中藤树,源代碼如下:

// sync/once.go

type Once struct {
   done uint32 // 初始值為0表示還未執(zhí)行過,1表示已經(jīng)執(zhí)行過
   m    Mutex 
}
func (o *Once) Do(f func()) {
   // 判斷done是否為0拓萌,若為0岁钓,表示未執(zhí)行過,調(diào)用doSlow()方法初始化
   if atomic.LoadUint32(&o.done) == 0 {
      // Outlined slow-path to allow inlining of the fast-path.
      o.doSlow(f)
   }
}

// 加載資源
func (o *Once) doSlow(f func()) {
   o.m.Lock()
   defer o.m.Unlock()
   // 采用雙重檢測機制 加鎖判斷done是否為零
   if o.done == 0 {
      // 執(zhí)行完f()函數(shù)后,將done值設置為1
      defer atomic.StoreUint32(&o.done, 1)
      // 執(zhí)行傳入的f()函數(shù)
      f()
   }
}

接下來會分為兩大部分進行分析屡限,第一部分為 Once 的結(jié)構(gòu)體組成結(jié)構(gòu)品嚣,第二部分為 Do 函數(shù)的實現(xiàn)原理,我會在代碼上加上注釋钧大,保證用心閱讀完都有收獲翰撑。

結(jié)構(gòu)體

type Once struct {
   done uint32 // 初始值為0表示還未執(zhí)行過,1表示已經(jīng)執(zhí)行過
   m    Mutex 
}

首先定義一個struct結(jié)構(gòu)體 Once 啊央,里面存儲兩個成員變量眶诈,分別為 donem

done成員變量

  • 1表示資源未初始化瓜饥,需要進一步初始化
  • 0表示資源已初始化册养,無需初始化,直接返回即可

m成員變量

  • 為了防止多個goroutine調(diào)用 doSlow() 初始化資源時压固,造成資源多次初始化,因此采用 Mutex 鎖機制來保證有且僅初始化一次

Do

func (o *Once) Do(f func()) {
   // 判斷done是否為0靠闭,若為0帐我,表示未執(zhí)行過,調(diào)用doSlow()方法初始化
   if atomic.LoadUint32(&o.done) == 0 {
      // Outlined slow-path to allow inlining of the fast-path.
      o.doSlow(f)
   }
}

// 加載資源
func (o *Once) doSlow(f func()) {
   o.m.Lock()
   defer o.m.Unlock()
   // 采用雙重檢測機制 加鎖判斷done是否為零
   if o.done == 0 {
      // 執(zhí)行完f()函數(shù)后愧膀,將done值設置為1
      defer atomic.StoreUint32(&o.done, 1)
      // 執(zhí)行傳入的f()函數(shù)
      f()
   }

調(diào)用 Do 函數(shù)時拦键,首先判斷done值是否為0,若為1檩淋,表示傳入的匿名函數(shù) f() 已執(zhí)行過芬为,無需再次執(zhí)行;若為0蟀悦,表示傳入的匿名函數(shù) f() 還未執(zhí)行過媚朦,則調(diào)用 doSlow() 函數(shù)進行初始化。

在 doSlow() 函數(shù)中日戈,若并發(fā)的goroutine進入該函數(shù)中询张,為了保證僅有一個goroutine執(zhí)行 f() 匿名函數(shù)。為此浙炼,需要加互斥鎖保證只有一個goroutine進行初始化份氧,同時采用了雙檢查的機制(double-checking),再次判斷 o.done 是否為 0弯屈,如果為 0蜗帜,則是第一次執(zhí)行,執(zhí)行完畢后资厉,就將 o.done 設置為 1厅缺,然后釋放鎖。

即使此時有多個 goroutine 同時進入了 doSlow 方法,因為雙檢查的機制店归,后續(xù)的 goroutine 會看到 o.done 的值為 1阎抒,也不會再次執(zhí)行 f。

這樣既保證了并發(fā)的 goroutine 會等待 f 完成消痛,而且還不會多次執(zhí)行 f且叁。

文章也會持續(xù)更新,可以微信搜索「 邁莫coding 」第一時間閱讀秩伞。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末逞带,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子纱新,更是在濱河造成了極大的恐慌展氓,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脸爱,死亡現(xiàn)場離奇詭異遇汞,居然都是意外死亡,警方通過查閱死者的電腦和手機簿废,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門空入,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人族檬,你說我怎么就攤上這事歪赢。” “怎么了单料?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵埋凯,是天一觀的道長。 經(jīng)常有香客問我扫尖,道長白对,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任换怖,我火速辦了婚禮躏结,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狰域。我一直安慰自己媳拴,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布兆览。 她就那樣靜靜地躺著屈溉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪抬探。 梳的紋絲不亂的頭發(fā)上子巾,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天帆赢,我揣著相機與錄音,去河邊找鬼线梗。 笑死椰于,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的仪搔。 我是一名探鬼主播瘾婿,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼烤咧!你這毒婦竟也來了偏陪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤煮嫌,失蹤者是張志新(化名)和其女友劉穎笛谦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昌阿,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡饥脑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了懦冰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片好啰。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖儿奶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鳄抒,我是刑警寧澤闯捎,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站许溅,受9級特大地震影響瓤鼻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贤重,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一茬祷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧并蝗,春花似錦祭犯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至键畴,卻和暖如春最盅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工涡贱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咏删,地道東北人。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓问词,卻偏偏與公主長得像督函,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子戏售,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內(nèi)容