亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入理解go sync.Once的具體使用

 更新時(shí)間:2024年01月09日 15:06:20   作者:rubys007  
在很多情況下,我們可能需要控制某一段代碼只執(zhí)行一次,go 為我們提供了?sync.Once?對(duì)象,它保證了某個(gè)動(dòng)作只被執(zhí)行一次,本文主要介紹了深入理解go sync.Once的具體使用,感興趣的可以了解一下

在很多情況下,我們可能需要控制某一段代碼只執(zhí)行一次,比如做某些初始化操作,如初始化數(shù)據(jù)庫(kù)連接等。

對(duì)于這種場(chǎng)景,go 為我們提供了 sync.Once 對(duì)象,它保證了某個(gè)動(dòng)作只被執(zhí)行一次。

當(dāng)然我們也是可以自己通過 Mutex 實(shí)現(xiàn) sync.Once 的功能,但是相比來說繁瑣了那么一點(diǎn),因?yàn)槲覀儾粌H要自己去控制鎖,還要通過一個(gè)標(biāo)識(shí)來標(biāo)志是否已經(jīng)執(zhí)行過。

Once 的實(shí)現(xiàn)

Once 的實(shí)現(xiàn)非常簡(jiǎ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()
	}
}

簡(jiǎn)要說明:

  • done 字段指示了操作是否已執(zhí)行,也就是我們傳遞給 Do 的函數(shù)是否已經(jīng)被執(zhí)行。
  • Do 方法接收一個(gè)函數(shù)參數(shù),這個(gè)函數(shù)參數(shù)只會(huì)被執(zhí)行一次。
  • Once 內(nèi)部是通過 Mutex 來實(shí)現(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 會(huì)調(diào)用 3 次,但最終只會(huì)執(zhí)行一次
			once.Do(test)

			wg.Done()
		}()
	}

	wg.Wait()

	fmt.Println(a) // 1
}

Once 的一些工作機(jī)制

  • Once 的 Do 方法可以保證,在多個(gè) goroutine 同時(shí)執(zhí)行 Do 方法的時(shí)候,
    在第一個(gè)搶占到 Do 執(zhí)行權(quán)的 goroutine 執(zhí)行返回之前,其他 goroutine 都會(huì)阻塞在 Once.Do 的調(diào)用上,
    只有第一個(gè) Do 調(diào)用返回的時(shí)候,其他 goroutine 才可以繼續(xù)執(zhí)行下去,并且其他所有的 goroutine
    不會(huì)再執(zhí)行傳遞給 Do 的函數(shù)。(如果是初始化的場(chǎng)景,這可以避免尚未初始化完成就執(zhí)行其他的操作)

  • 如果 Once.Do 發(fā)生 panic 的時(shí)候,傳遞給 Do 的函數(shù)依然被標(biāo)記為已完成。后續(xù)對(duì) Do 的調(diào)用也不會(huì)再執(zhí)行傳給 Do 的函數(shù)參數(shù)。

  • 我們不能簡(jiǎn)單地通過 atomic.CompareAndSwapUint32 來決定是否執(zhí)行 f(),因?yàn)樵诙鄠€(gè) goroutine 同時(shí)執(zhí)行的時(shí)候,它無法保證 f() 只被執(zhí)行一次。所以 Once 里面用了 Mutex,這樣就可以有效地保護(hù)臨界區(qū)。

// 錯(cuò)誤實(shí)現(xiàn),這不能保證 f 只被執(zhí)行一次
if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
    f()
}
  • Once.Do 的函數(shù)參數(shù)是沒有參數(shù)的,如果我們需要傳遞一些參數(shù),可以再對(duì) f 做一層包裹。
config.once.Do(func() { config.init(filename) })

Once 詳解

hotpath

這里說的 hotpath 指的是 Once 里的第一個(gè)字段 done

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

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

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

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 字段偏移量 計(jì)算得出。
	// 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 的,
這是因?yàn)?,如果直接使?nbsp;done == 0 的方式的話,就有可能導(dǎo)致在 doSlow 里面對(duì) done 設(shè)置為 1 之后,
在 Do 方法里面無法正常觀測(cè)到。因此用了 atomic.LoadUint32

而在 doSlow 里面是可以通過 done == 0 來判斷的,這是因?yàn)?nbsp;doSlow 里面已經(jīng)通過 Mutex 保護(hù)起來了。
唯一設(shè)置 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(&o.done, 1)
		f()
	}
}

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

Mutex

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

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

總結(jié)

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

到此這篇關(guān)于深入理解go sync.Once的具體使用的文章就介紹到這了,更多相關(guān)go sync.Once內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • Golang使用Gin框架實(shí)現(xiàn)HTTP響應(yīng)格式統(tǒng)一處理

    Golang使用Gin框架實(shí)現(xiàn)HTTP響應(yīng)格式統(tǒng)一處理

    在gin框架中,我們可以定義一個(gè)中間件來處理統(tǒng)一的HTTP響應(yīng)格式,本文主要為大家介紹了具體是怎么定義實(shí)現(xiàn)這樣的中間件的,感興趣的小伙伴可以了解一下
    2023-07-07
  • Golang極簡(jiǎn)入門教程(一):基本概念

    Golang極簡(jiǎn)入門教程(一):基本概念

    這篇文章主要介紹了Golang極簡(jiǎn)入門教程(一):基本概念,本文講解了Golang的基本知識(shí)、基礎(chǔ)語(yǔ)法、相關(guān)術(shù)語(yǔ)等,需要的朋友可以參考下
    2014-10-10
  • go語(yǔ)言中fallthrough的用法說明

    go語(yǔ)言中fallthrough的用法說明

    這篇文章主要介紹了go語(yǔ)言中fallthrough的用法說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • 初學(xué)Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全

    初學(xué)Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全

    這篇文章主要給大家介紹了關(guān)于初學(xué)vscode寫Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全的相關(guān)資料,由于vscode是開源免費(fèi)的,而且開發(fā)支持vscode的插件相對(duì)比較容易,更新速度也很快,需要的朋友可以參考下
    2023-07-07
  • 使用Go語(yǔ)言生成二維碼并在命令行中輸出

    使用Go語(yǔ)言生成二維碼并在命令行中輸出

    二維碼(QR code)是一種矩陣條碼的標(biāo)準(zhǔn),廣泛應(yīng)用于商業(yè)、移動(dòng)支付和數(shù)據(jù)存儲(chǔ)等領(lǐng)域,在開發(fā)過程中,我們可能需要在命令行中顯示二維碼,這可以幫助我們快速生成和分享二維碼信息,本文將介紹如何使用Go語(yǔ)言生成二維碼并在命令行中輸出,需要的朋友可以參考下
    2023-11-11
  • goland2020.2.x永久激活碼破解詳細(xì)教程親測(cè)可用(Windows Linux Mac)

    goland2020.2.x永久激活碼破解詳細(xì)教程親測(cè)可用(Windows Linux Mac)

    這篇文章主要介紹了goland2020.2.x永久激活碼破解詳細(xì)教程親測(cè)可用(Windows Linux Mac) ,對(duì)goland激活碼注冊(cè)碼相關(guān)知識(shí)感興趣的朋友跟隨小編一起看看吧
    2020-11-11
  • GO語(yǔ)言原生實(shí)現(xiàn)文件上傳功能

    GO語(yǔ)言原生實(shí)現(xiàn)文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了GO語(yǔ)言原生實(shí)現(xiàn)文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Go/C語(yǔ)言LeetCode題解997找到小鎮(zhèn)法官

    Go/C語(yǔ)言LeetCode題解997找到小鎮(zhèn)法官

    這篇文章主要為大家介紹了Go語(yǔ)言LeetCode題解997找到小鎮(zhèn)的法官示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • 一文帶大家了解Go語(yǔ)言中的內(nèi)聯(lián)優(yōu)化

    一文帶大家了解Go語(yǔ)言中的內(nèi)聯(lián)優(yōu)化

    內(nèi)聯(lián)優(yōu)化是一種常見的編譯器優(yōu)化策略,通俗來講,就是把函數(shù)在它被調(diào)用的地方展開,這樣可以減少函數(shù)調(diào)用所帶來的開銷,本文主要為大家介紹了Go中內(nèi)聯(lián)優(yōu)化的具體使用,需要的可以參考下
    2023-05-05
  • Go如何優(yōu)雅的使用字節(jié)池示例詳解

    Go如何優(yōu)雅的使用字節(jié)池示例詳解

    在編程開發(fā)中,我們經(jīng)常會(huì)需要頻繁創(chuàng)建和銷毀同類對(duì)象的情形,這樣的操作很可能會(huì)對(duì)性能造成影響,這時(shí)常用的優(yōu)化手段就是使用對(duì)象池(object pool),這篇文章主要給大家介紹了關(guān)于Go如何優(yōu)雅的使用字節(jié)池的相關(guān)資料,需要的朋友可以參考下
    2022-08-08

最新評(píng)論