Golang WaitGroup源碼分析

來源:互聯網
上載者:User

針對Golang 1.9的sync.WaitGroup進行分析,與Golang 1.10基本一樣除了將panic改為了throw之外其他的都一樣。
原始碼位置:sync\waitgroup.go

結構體

type WaitGroup struct {    noCopy noCopy  // noCopy可以嵌入到結構中,在第一次使用後不可複製,使用go vet作為檢測使用    // 位值:高32位是計數器,低32位是goroution等待計數。    // 64位的原子操作需要64位的對齊,但是32位。編譯器不能確保它,所以分配了12個byte對齊的8個byte作為狀態。    state1 [12]byte // byte=uint8範圍:0~255,只取前8個元素。轉為2進位:0000 0000,0000 0000... ...0000 0000    sema   uint32   // 訊號量,用於喚醒goroution}

不知道大家是否和我一樣,不論是使用Java的CountDownLatch還是Golang的WaitGroup,都會疑問,可以裝下多個線程|協程等待呢?看了源碼後可以回答了,可以裝下

1111 1111 1111 ... 1111\________32___________/

2^32個辣麼多!所以不需要擔心單機情況下會被撐爆了。

函數

以下代碼已經去掉了與核心代碼無關的race代碼。

Add

添加或者減少等待goroutine的數量。

參數delta可能是負的,加到WaitGroup計數器,可能出現如下結果

  • 如果計數器變為零,所有被阻塞的goroutines都會被釋放。
  • 如果計數器變成負數,就增加恐慌。
func (wg *WaitGroup) Add(delta int) {    // 擷取到wg.state1數組中元素組成的二進位對應的十進位的值    statep := wg.state()    // 高32位是計數器    state := atomic.AddUint64(statep, uint64(delta)<<32)    // 擷取計數器    v := int32(state >> 32)    w := uint32(state)    // 計數器為負數,報panic    if v < 0 {        panic("sync: negative WaitGroup counter")    }    // 添加與等待並發調用,報panic    if w != 0 && delta > 0 && v == int32(delta) {        panic("sync: WaitGroup misuse: Add called concurrently with Wait")    }    // 計數器添加成功    if v > 0 || w == 0 {        return    }    // 當等待計數器> 0時,而goroutine設定為0。    // 此時不可能有同時發生的狀態突變:    // - 增加不能與等待同時發生,    // - 如果計數器counter == 0,不再增加等待計數器    if *statep != state {        panic("sync: WaitGroup misuse: Add called concurrently with Wait")    }    // Reset waiters count to 0.    *statep = 0    for ; w != 0; w-- {        // 目的是作為一個簡單的wakeup原語,以供同步使用。true為喚醒排在等待隊列的第一個goroutine        runtime_Semrelease(&wg.sema, false)    }}
// unsafe.Pointer其實就是類似C的void *,在golang中是用於各種指標相互轉換的橋樑。// uintptr是golang的內建類型,是能儲存指標的整型,uintptr的底層類型是int,它和unsafe.Pointer可相互轉換。// uintptr和unsafe.Pointer的區別就是:unsafe.Pointer只是單純的通用指標類型,用於轉換不同類型指標,它不可以參與指標運算;// 而uintptr是用於指標運算的,GC 不把 uintptr 當指標,也就是說 uintptr 無法持有對象,uintptr類型的目標會被回收。// state()函數可以擷取到wg.state1數組中元素組成的二進位對應的十進位的值func (wg *WaitGroup) state() *uint64 {    if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {        return (*uint64)(unsafe.Pointer(&wg.state1))    } else {        return (*uint64)(unsafe.Pointer(&wg.state1[4]))    }}

Done

相當於Add(-1)。

func (wg *WaitGroup) Done() {    // 計數器減一    wg.Add(-1)}

Wait

執行阻塞,直到所有的WaitGroup數量變成0。

func (wg *WaitGroup) Wait() {    // 擷取到wg.state1數組中元素組成的二進位對應的十進位的值    statep := wg.state()    // cas演算法    for {        state := atomic.LoadUint64(statep)        // 高32位是計數器        v := int32(state >> 32)        w := uint32(state)        // 計數器為0,結束等待        if v == 0 {            // Counter is 0, no need to wait.            return        }        // 增加等待goroution計數,對低32位加1,不需要移位        if atomic.CompareAndSwapUint64(statep, state, state+1) {            // 目的是作為一個簡單的sleep原語,以供同步使用            runtime_Semacquire(&wg.sema)            if *statep != 0 {                panic("sync: WaitGroup is reused before previous Wait has returned")            }            return        }    }}

使用注意事項

  1. WaitGroup不能保證多個 goroutine 執行次序
  2. WaitGroup無法指定固定的goroutine數目
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.