欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go sync Once實現(xiàn)原理示例解析

 更新時間:2023年01月03日 11:18:12   作者:eleven26  
這篇文章主要為大家介紹了go sync Once實現(xiàn)原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

在很多情況下,我們可能需要控制某一段代碼只執(zhí)行一次,比如做某些初始化操作,如初始化數(shù)據(jù)庫連接等。 對于這種場景,go 為我們提供了 sync.Once 對象,它保證了某個動作只被執(zhí)行一次。 當然我們也是可以自己通過 Mutex 實現(xiàn) sync.Once 的功能,但是相比來說繁瑣了那么一點, 因為我們不僅要自己去控制鎖,還要通過一個標識來標志是否已經(jīng)執(zhí)行過。

Once 的實現(xiàn)

Once 的實現(xiàn)非常簡單,如下,就只有 20 來行代碼,但里面包含了 go 并發(fā)、同步的一些常見處理方法。

package sync
import (
   "sync/atomic"
)
type Once struct {
   done uint32
   m    Mutex
}
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()
   }
}

簡要說明:

  • done 字段指示了操作是否已執(zhí)行,也就是我們傳遞給 Do 的函數(shù)是否已經(jīng)被執(zhí)行。
  • Do 方法接收一個函數(shù)參數(shù),這個函數(shù)參數(shù)只會被執(zhí)行一次。
  • Once 內(nèi)部是通過 Mutex 來實現(xiàn)不同協(xié)程之間的同步的。

使用示例

在下面的例子中,once.Do(test) 被執(zhí)行了 3 次,但是最終 test 只被執(zhí)行了一次。

package sync
import (
   "fmt"
   "sync"
   "testing"
)
var once sync.Once
var a = 0
func test() {
   a++
}
func TestOnce(t *testing.T) {
   var wg sync.WaitGroup
   wg.Add(3)
   for i := 0; i < 3; i++ {
      go func() {
         // once.Do 會調(diào)用 3 次,但最終只會執(zhí)行一次
         once.Do(test)
         wg.Done()
      }()
   }
   wg.Wait()
   fmt.Println(a) // 1
}

Once 的一些工作機制

  • OnceDo 方法可以保證,在多個 goroutine 同時執(zhí)行 Do 方法的時候, 在第一個搶占到 Do 執(zhí)行權(quán)的 goroutine 執(zhí)行返回之前,其他 goroutine 都會阻塞在 Once.Do 的調(diào)用上, 只有第一個 Do 調(diào)用返回的時候,其他 goroutine 才可以繼續(xù)執(zhí)行下去,并且其他所有的 goroutine 不會再執(zhí)行傳遞給 Do 的函數(shù)。(如果是初始化的場景,這可以避免尚未初始化完成就執(zhí)行其他的操作)
  • 如果 Once.Do 發(fā)生 panic 的時候,傳遞給 Do 的函數(shù)依然被標記為已完成。后續(xù)對 Do 的調(diào)用也不會再執(zhí)行傳給 Do 的函數(shù)參數(shù)。
  • 我們不能簡單地通過 atomic.CompareAndSwapUint32 來決定是否執(zhí)行 f(),因為在多個 goroutine 同時執(zhí)行的時候,它無法保證 f() 只被執(zhí)行一次。所以 Once 里面用了 Mutex,這樣就可以有效地保護臨界區(qū)。
// 錯誤實現(xiàn),這不能保證 f 只被執(zhí)行一次
if atomic.CompareAndSwapUint32(&amp;o.done, 0, 1) {
    f()
}
  • Once.Do 的函數(shù)參數(shù)是沒有參數(shù)的,如果我們需要傳遞一些參數(shù),可以再對 f 做一層包裹。
config.once.Do(func() { config.init(filename) })

Once 詳解

hotpath

這里說的 hotpath 指的是 Once 里的第一個字段 done

type Once struct {
   // hotpath
   done uint32
   m    Mutex
}

Once 結(jié)構(gòu)體的第一個字段是 done,這是因為 done 的訪問是遠遠大于 Once 中另外一個字段 m 的, 放在第一個字段中,編譯器就可以做一些優(yōu)化,因為結(jié)構(gòu)體的地址其實就是結(jié)構(gòu)體第一個字段的地址, 這樣一來,在訪問 done 字段的時候,就不需要通過結(jié)構(gòu)體地址 + 偏移量的方式來訪問, 這在一定程度上提高了性能。

結(jié)構(gòu)體地址計算示例:

type person struct {
   name string
   age  int
}
func TestStruct(t *testing.T) {
   var p = person{
      name: "foo",
      age:  10,
   }
   // p 和 p.name 的地址相同
   // 0xc0000100a8, 0xc0000100a8
   fmt.Printf("%p, %p\n", &p, &p.name)
   // p.age 的地址
   // 0xc0000100b8
   fmt.Printf("%p\n", &p.age)
   // p.age 的地址也可以通過:結(jié)構(gòu)體地址 + age 字段偏移量 計算得出。
   // 0xc0000100b8
   fmt.Println(unsafe.Add(unsafe.Pointer(&p), unsafe.Offsetof(p.age)))
}

atomic.LoadUint32

func (o *Once) Do(f func()) {
   if atomic.LoadUint32(&o.done) == 0 {
      o.doSlow(f)
   }
}

Do 方法中,是通過 atomic.LoadUint32 的方式來判斷 done 是否等于 0 的, 這是因為,如果直接使用 done == 0 的方式的話,就有可能導致在 doSlow 里面對 done 設置為 1 之后, 在 Do 方法里面無法正常觀測到。因此用了 atomic.LoadUint32

而在 doSlow 里面是可以通過 done == 0 來判斷的,這是因為 doSlow 里面已經(jīng)通過 Mutex 保護起來了。 唯一設置 done = 1 的地方就在臨界區(qū)里面,所以 doSlow 里面通過 done == 0 來判斷是完全沒有問題的。

atomic.StoreUint32

func (o *Once) doSlow(f func()) {
   o.m.Lock()
   defer o.m.Unlock()
   if o.done == 0 {
      defer atomic.StoreUint32(&amp;o.done, 1)
      f()
   }
}

doSlow 方法中,設置 done 為 1 也是通過 atomic.StoreUint32 來設置的。 這樣就可以保證在設置了 done 為 1 之后,可以及時被其他 goroutine 看到。

Mutex

doSlow 的實現(xiàn)里面,最終還是要通過 Mutex 來保護臨界區(qū), 通過 Mutex 可以實現(xiàn) f 只被執(zhí)行一次,并且其他的 goroutine 都可以使用這一次 f 的執(zhí)行結(jié)果。 因為其他 goroutine 在第一次 f 調(diào)用未返回之前,都阻塞在獲取 Mutex 鎖的地方, 當它們獲取到 Mutex 鎖的時候,得以繼續(xù)往下執(zhí)行,但這個時候 f 已經(jīng)執(zhí)行完畢了, 所以當它們獲取到 Mutex 鎖之后其實什么也沒有干。

但是它們的阻塞狀態(tài)被解除了,可以繼續(xù)往下執(zhí)行。

總結(jié)

  • Once 保證了傳入的函數(shù)只會執(zhí)行一次,這常常用在一些初始化的場景、或者單例模式。
  • Once 可以保證所有對 Do 的并發(fā)調(diào)用都是安全的,所有對 Once.Do 調(diào)用之后的操作,一定會在第一次對 f 調(diào)用之后執(zhí)行。(沒有獲取到 f 執(zhí)行權(quán)的 goroutine 會阻塞)
  • 即使 Once.Do 里面的 f 出現(xiàn)了 panic,后續(xù)也不會再次調(diào)用 f。

以上就是go sync Once實現(xiàn)原理示例解析的詳細內(nèi)容,更多關于go sync Once實現(xiàn)原理的資料請關注腳本之家其它相關文章!

相關文章

  • golang判斷文本文件是否是BOM格式的方法詳解

    golang判斷文本文件是否是BOM格式的方法詳解

    在Go語言中,我們可以通過讀取文本文件的前幾個字節(jié)來識別它是否是BOM格式的文件,BOM(Byte Order Mark)是UTF編碼標準中的一部分,用于標示文本文件的編碼順序,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2023-10-10
  • 使用docker構(gòu)建golang線上部署環(huán)境的步驟詳解

    使用docker構(gòu)建golang線上部署環(huán)境的步驟詳解

    這篇文章主要介紹了使用docker構(gòu)建golang線上部署環(huán)境的步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。
    2017-11-11
  • 詳解Go語言中配置文件使用與日志配置

    詳解Go語言中配置文件使用與日志配置

    這篇文章主要為大家詳細講解一下Go語言中調(diào)整項目目錄結(jié)構(gòu)、增加配置文件使用和增加日志配置的方法,文中示例代碼講解詳細,需要的可以參考一下
    2022-06-06
  • Golang中漏洞數(shù)據(jù)庫的使用詳解

    Golang中漏洞數(shù)據(jù)庫的使用詳解

    govulncheck是Golang中的漏洞掃描工具,它強大功能的背后,離不開?Go?漏洞數(shù)據(jù)庫(Go?vulnerability?database)的支持,所以本文就來為大家詳細講解下?Go?漏洞數(shù)據(jù)庫相關的知識
    2023-09-09
  • go語言Timer計時器的用法示例詳解

    go語言Timer計時器的用法示例詳解

    Go語言的標準庫里提供兩種類型的計時器Timer和Ticker。這篇文章通過實例代碼給大家介紹go語言Timer計時器的用法,代碼簡單易懂,感興趣的朋友跟隨小編一起看看吧
    2020-05-05
  • 利用Go語言實現(xiàn)簡單Ping過程的方法

    利用Go語言實現(xiàn)簡單Ping過程的方法

    相信利用各種語言實現(xiàn)Ping已經(jīng)是大家喜聞樂見的事情了,網(wǎng)絡上利用Golang實現(xiàn)Ping已經(jīng)有比較詳細的代碼示例,但大多是僅僅是實現(xiàn)了Request過程,而對Response的回顯內(nèi)容并沒有做接收。而Ping程序不僅僅是發(fā)送一個ICMP,更重要的是如何接收并進行統(tǒng)計。
    2016-09-09
  • Go項目編寫Makefile規(guī)則文件概述

    Go項目編寫Makefile規(guī)則文件概述

    這篇文章主要為大家介紹了Go項目編寫Makefile文件規(guī)則概述,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • go-micro微服務JWT跨域認證問題

    go-micro微服務JWT跨域認證問題

    JWT 以 JSON 對象的形式安全傳遞信息。因為存在數(shù)字簽名,因此所傳遞的信息是安全的,這篇文章主要介紹了go-micro微服務JWT跨域認證,需要的朋友可以參考下
    2023-01-01
  • Golang實現(xiàn)解析JSON的三種方法總結(jié)

    Golang實現(xiàn)解析JSON的三種方法總結(jié)

    這篇文章主要為大家詳細介紹了Golang實現(xiàn)解析JSON的三種方法,文中的示例代碼講解詳細,對我們學習了解JSON有一定幫助,需要的可以參考一下
    2022-09-09
  • 一文帶你深入了解Golang中的自旋鎖

    一文帶你深入了解Golang中的自旋鎖

    自旋鎖是一種忙等待鎖,當一個線程嘗試獲取一個已經(jīng)被其它線程持有的鎖時,這個線程會持續(xù)循環(huán)檢查鎖的狀態(tài)(即“自旋”)?,直到鎖被釋放后獲得所有權(quán),下面我們就來深入了解下自旋鎖的具體操作吧
    2024-01-01

最新評論