最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang WaitGroup實(shí)現(xiàn)原理解析

 更新時(shí)間:2023年02月03日 09:07:41   作者:raoxiaoya  
WaitGroup是Golang并發(fā)的兩種方式之一,一個(gè)是Channel,另一個(gè)是WaitGroup,下面這篇文章主要給大家介紹了關(guān)于golang基礎(chǔ)之waitgroup用法以及使用要點(diǎn)的相關(guān)資料,需要的朋友可以參考下

原理解析

type WaitGroup struct {
   noCopy noCopy
   // 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
   // 64-bit atomic operations require 64-bit alignment, but 32-bit
   // compilers only guarantee that 64-bit fields are 32-bit aligned.
   // For this reason on 32 bit architectures we need to check in state()
   // if state1 is aligned or not, and dynamically "swap" the field order if
   // needed.
   state1 uint64
   state2 uint32
}

其中 noCopy 是 golang 源碼中檢測禁止拷貝的技術(shù)。如果程序中有 WaitGroup 的賦值行為,使用 go vet 檢查程序時(shí),就會(huì)發(fā)現(xiàn)有報(bào)錯(cuò)。但需要注意的是,noCopy 不會(huì)影響程序正常的編譯和運(yùn)行。

state1字段

  • 高32位為counter,代表目前尚未完成的協(xié)程個(gè)數(shù)。
  • 低32位為waiter,代表目前已調(diào)用 Wait 的 goroutine 的個(gè)數(shù),因?yàn)?code>wait可以被多個(gè)協(xié)程調(diào)用。

state2為信號(hào)量。

WaitGroup 的整個(gè)調(diào)用過程可以簡單地描述成下面這樣:

  • 當(dāng)調(diào)用 WaitGroup.Add(n) 時(shí),counter 將會(huì)自增: counter + n
  • 當(dāng)調(diào)用 WaitGroup.Wait() 時(shí),會(huì)將 waiter++。同時(shí)調(diào)用 runtime_Semacquire(semap), 增加信號(hào)量,并掛起當(dāng)前 goroutine。
  • 當(dāng)調(diào)用 WaitGroup.Done() 時(shí),將會(huì) counter--。如果自減后的 counter 等于 0,說明 WaitGroup 的等待過程已經(jīng)結(jié)束,則需要調(diào)用 runtime_Semrelease 釋放信號(hào)量,喚醒正在 WaitGroup.Wait 的 goroutine。

關(guān)于內(nèi)存對(duì)其

func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {
	if unsafe.Alignof(wg.state1) == 8 || uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {
		// state1 is 64-bit aligned: nothing to do.
		return &wg.state1, &wg.state2
	} else {
		// state1 is 32-bit aligned but not 64-bit aligned: this means that
		// (&state1)+4 is 64-bit aligned.
		state := (*[3]uint32)(unsafe.Pointer(&wg.state1))
		return (*uint64)(unsafe.Pointer(&state[1])), &state[0]
	}
}

如果變量是 64 位對(duì)齊 (8 byte), 則該變量的起始地址是 8 的倍數(shù)。如果變量是 32 位對(duì)齊 (4 byte),則該變量的起始地址是 4 的倍數(shù)。

當(dāng) state1 是 32 位的時(shí)候,那么state1被當(dāng)成是一個(gè)數(shù)組[3]uint32,數(shù)組的第一位是semap,第二三位存儲(chǔ)著counter, waiter正好是64位。

為什么會(huì)有這種奇怪的設(shè)定呢?這里涉及兩個(gè)前提:

前提 1:在 WaitGroup 的真實(shí)邏輯中, counter 和 waiter 被合在了一起,當(dāng)成一個(gè) 64 位的整數(shù)對(duì)外使用。當(dāng)需要變化 counter 和 waiter 的值的時(shí)候,也是通過 atomic 來原子操作這個(gè) 64 位整數(shù)。

前提 2:在 32 位系統(tǒng)下,如果使用 atomic 對(duì) 64 位變量進(jìn)行原子操作,調(diào)用者需要自行保證變量的 64 位對(duì)齊,否則將會(huì)出現(xiàn)異常。golang 的官方文檔 sync/atomic/#pkg-note-BUG 原文是這么說的:

On ARM, x86-32, and 32-bit MIPS, it is the caller’s responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.

因此,在前提 1 的情況下,WaitGroup 需要對(duì) 64 位進(jìn)行原子操作。根據(jù)前提 2,WaitGroup 需要自行保證 count+waiter 的 64 位對(duì)齊。

這個(gè)方法非常的巧妙,只不過是改變 semap 的位置順序,就既可以保證 counter+waiter 一定會(huì) 64 位對(duì)齊,也可以保證內(nèi)存的高效利用。

注: 有些文章會(huì)講到,WaitGroup 兩種不同的內(nèi)存布局方式是 32 位系統(tǒng)和 64 位系統(tǒng)的區(qū)別,這其實(shí)不太嚴(yán)謹(jǐn)。準(zhǔn)確的說法是 32 位對(duì)齊和 64 位對(duì)齊的區(qū)別。因?yàn)樵?32 位系統(tǒng)下,state1 變量也有可能恰好符合 64 位對(duì)齊。

sync.mutex的源碼中就沒有出現(xiàn)內(nèi)存對(duì)其的操作,雖然它也有大量的atomic操作,那是因?yàn)?code>state int32。

sync.mutex中也是將四個(gè)狀態(tài)存在一個(gè)變量地址,其實(shí)這么做的目的就是為了實(shí)現(xiàn)原子操作,因?yàn)闆]有辦法同時(shí)修改多個(gè)變量還要保證原子性。

WaitGroup 直接把 counterwaiter 看成了一個(gè)統(tǒng)一的 64 位變量。其中 counter 是這個(gè)變量的高 32 位,waiter 是這個(gè)變量的低 32 位。 在需要改變 counter 時(shí), 通過將累加值左移 32 位的方式。

這里的原子操作并沒有使用Mutex或者RWMutex這樣的鎖,主要是因?yàn)殒i會(huì)帶來不小的性能損耗,存在上下文切換,而對(duì)于單個(gè)內(nèi)存地址的原子操作最好的方式是atomic,因?yàn)檫@是由底層硬件提供的支持(CPU指令),粒度更小,性能更高。

源碼部分

func (wg *WaitGroup) Add(delta int) {
    // wg.state()返回的是地址
	statep, semap := wg.state()
    // 原子操作,修改statep高32位的值,即counter的值
	state := atomic.AddUint64(statep, uint64(delta)<<32)
    // 右移32位,使高32位變成了低32,得到counter的值
	v := int32(state >> 32)
    // 直接取低32位,得到waiter的值
	w := uint32(state)
	// 不規(guī)范的操作
	if v < 0 {
		panic("sync: negative WaitGroup counter")
	}
    // 不規(guī)范的操作
	if w != 0 && delta > 0 && v == int32(delta) {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
    // 這是正常的情況
	if v > 0 || w == 0 {
		return
	}
    // 剩下的就是 counter == 0 且 waiter != 0 的情況
    // 在這個(gè)情況下,*statep 的值就是 waiter 的值,否則就有問題
    // 在這個(gè)情況下,所有的任務(wù)都已經(jīng)完成,可以將 *statep 整個(gè)置0
    // 同時(shí)向所有的Waiter釋放信號(hào)量
	// This goroutine has set counter to 0 when waiters > 0.
	// Now there can't be concurrent mutations of state:
	// - Adds must not happen concurrently with Wait,
	// - Wait does not increment waiters if it sees counter == 0.
	// Still do a cheap sanity check to detect WaitGroup misuse.
	if *statep != state {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	// Reset waiters count to 0.
	*statep = 0
	for ; w != 0; w-- {
		runtime_Semrelease(semap, false, 0)
	}
}
func (wg *WaitGroup) Done() {
	wg.Add(-1)
}
func (wg *WaitGroup) Wait() {
    // wg.state()返回的是地址
	statep, semap := wg.state()
    // for循環(huán)是配合CAS操作
	for {
		state := atomic.LoadUint64(statep)
		v := int32(state >> 32) // counter
		w := uint32(state) // waiter
        // 如果counter為0,說明所有的任務(wù)在調(diào)用Wait的時(shí)候就已經(jīng)完成了,直接退出
        // 這就要求,必須在同步的情況下調(diào)用Add(),否則Wait可能先退出了
		if v == 0 {
			return
		}
		// waiter++,原子操作
		if atomic.CompareAndSwapUint64(statep, state, state+1) {
            // 如果自增成功,則獲取信號(hào)量,此處信號(hào)量起到了同步的作用
			runtime_Semacquire(semap)
			return
		}
	}
}

總結(jié)一下,WaitGroup 的原理就五個(gè)點(diǎn):內(nèi)存對(duì)齊,原子操作,counter,waiter,信號(hào)量。

  • 內(nèi)存對(duì)齊的作用是為了原子操作。
  • counter的增減使用原子操作,counter的作用是一旦為0就釋放全部信號(hào)量。
  • waiter的自增使用原子操作,waiter的作用是表明要釋放多少信號(hào)量。

到此這篇關(guān)于Golang WaitGroup實(shí)現(xiàn)原理解析的文章就介紹到這了,更多相關(guān)Go WaitGroup內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文帶你感受Go語言空結(jié)構(gòu)體的魔力

    一文帶你感受Go語言空結(jié)構(gòu)體的魔力

    在?Go?語言中,有一種特殊的用法可能讓許多人感到困惑,那就是空結(jié)構(gòu)體,本文將對(duì)Go空結(jié)構(gòu)體進(jìn)行詳解,準(zhǔn)備一杯你最喜歡的飲料或茶,隨著本文一探究竟吧
    2023-05-05
  • golang數(shù)組內(nèi)存分配原理

    golang數(shù)組內(nèi)存分配原理

    這篇文章主要介紹了golang數(shù)組內(nèi)存分配原理,數(shù)組是內(nèi)存中一片連續(xù)的區(qū)域,在聲明時(shí)需要指定長度,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,感興趣的小伙伴可以參考一下
    2022-06-06
  • Golang中類型轉(zhuǎn)換利器cast庫的用法詳解

    Golang中類型轉(zhuǎn)換利器cast庫的用法詳解

    cast庫是一個(gè)簡潔而強(qiáng)大的第三方庫,它的主要功能是實(shí)現(xiàn)類型之間的安全轉(zhuǎn)換,而在Golang開發(fā)中,類型轉(zhuǎn)換是一個(gè)常見且不可避免的過程,下面我們就來看看cast庫在Golang中的具體應(yīng)用吧
    2024-11-11
  • Go語言中普通函數(shù)與方法的區(qū)別分析

    Go語言中普通函數(shù)與方法的區(qū)別分析

    這篇文章主要介紹了Go語言中普通函數(shù)與方法的區(qū)別,以實(shí)例形式對(duì)比分析了普通函數(shù)與方法使用時(shí)的區(qū)別與相關(guān)技巧,需要的朋友可以參考下
    2015-02-02
  • 使用Golang?Validator包實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證詳解

    使用Golang?Validator包實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證詳解

    在開發(fā)過程中,數(shù)據(jù)驗(yàn)證是一個(gè)非常重要的環(huán)節(jié),而golang中的Validator包是一個(gè)非常常用和強(qiáng)大的數(shù)據(jù)驗(yàn)證工具,提供了簡單易用的API和豐富的驗(yàn)證規(guī)則,下面我們就來看看Validator包的具體使用吧
    2023-12-12
  • Go常問的一些面試題匯總(附答案)

    Go常問的一些面試題匯總(附答案)

    通常我們?nèi)ッ嬖嚳隙〞?huì)有些不錯(cuò)的Golang的面試題目的,所以總結(jié)下,讓其他Golang開發(fā)者也可以查看到,同時(shí)也用來檢測自己的能力和提醒自己的不足之處,這篇文章主要給大家介紹了關(guān)于Go常問的一些面試題以及答案的相關(guān)資料,需要的朋友可以參考下
    2023-10-10
  • 基于Go語言簡單實(shí)現(xiàn)事件管理器

    基于Go語言簡單實(shí)現(xiàn)事件管理器

    在編程中,事件管理器是一種常見的工具,用于通過通知來觸發(fā)操作,本文將介紹一個(gè)簡單的Go事件管理器的實(shí)現(xiàn),并通過異步改進(jìn)提高其性能,感興趣的可以了解下
    2023-11-11
  • Golang中的信號(hào)(Signal)機(jī)制詳解

    Golang中的信號(hào)(Signal)機(jī)制詳解

    Signal 是一種操作系統(tǒng)級(jí)別的事件通知機(jī)制,進(jìn)程可以響應(yīng)特定的系統(tǒng)信號(hào),這些信號(hào)用于指示進(jìn)程執(zhí)行特定的操作,如程序終止、掛起、恢復(fù)等,Golang 的標(biāo)準(zhǔn)庫 os/signal 提供了對(duì)信號(hào)處理的支持,本文將詳細(xì)講解 Golang 是如何處理和響應(yīng)系統(tǒng)信號(hào)的,需要的朋友可以參考下
    2024-01-01
  • Golang控制協(xié)程執(zhí)行順序方法詳解

    Golang控制協(xié)程執(zhí)行順序方法詳解

    這篇文章主要介紹了Golang控制協(xié)程執(zhí)行順序的方法,Golang的語法和運(yùn)行時(shí)直接內(nèi)置了對(duì)并發(fā)的支持。Golang里的并發(fā)指的是能讓某個(gè)函數(shù)獨(dú)立于其他函數(shù)運(yùn)行的能力
    2022-11-11
  • 深入了解GoLang中的工廠設(shè)計(jì)模式

    深入了解GoLang中的工廠設(shè)計(jì)模式

    這篇文章主要介紹了深入了解GoLang中的工廠設(shè)計(jì)模式,工廠模式是一種常用的設(shè)計(jì)模式,它屬于創(chuàng)建型模式,它的主要目的是封裝對(duì)象的創(chuàng)建過程,將對(duì)象的創(chuàng)建過程與對(duì)象的使用過程分離,從而提高代碼的可維護(hù)性和可擴(kuò)展性,需要詳細(xì)了解可以參考下文
    2023-05-05

最新評(píng)論

汉寿县| 罗江县| 崇仁县| 十堰市| 内乡县| 错那县| 岑巩县| 四平市| 泸定县| 咸宁市| 富裕县| 海城市| 大关县| 永兴县| 宿迁市| 蚌埠市| 武陟县| 呈贡县| 青川县| 和顺县| 得荣县| 安平县| 满洲里市| 五莲县| 肃南| 宁明县| 柏乡县| 随州市| 当雄县| 抚宁县| 桑植县| 金华市| 高碑店市| 堆龙德庆县| 资阳市| 平罗县| 汾西县| 兰西县| 贵定县| 城步| 莱西市|