golang語言非同步通訊之WaitGroup

weixin_33890499發表於2018-02-07

golang語言非同步通訊之WaitGroup

簡介

WaitGroup的用途是使得主執行緒一直阻塞等待直到所有相關的子goroutine都已經完成了任務。

sync.WaitGroup只有3個API

  1. Add() # 新增計數
  2. Done() # 減掉計數,等價於Add(-1),這樣sync.WaitGroup只有兩個API了
  3. Wait() # 阻塞直到計數為零

用法例子1:正常用法


var wg sync.WaitGroup

func foo1() {
    log.Println("entry foo1")
    time.Sleep(5 * time.Second)
    wg.Done()
    log.Println("exit foo1")
}


func foo2() {
    log.Println("entry foo2")
    time.Sleep(2 * time.Second)
    wg.Done()
    log.Println("exit foo2")
}

func main() {
    log.Println("entry main")

    wg.Add(1)
    go foo1()

    wg.Add(1)
    go foo2()

    log.Println("wg.Wait()")
    wg.Wait()
    
    log.Println("exit main")
}

主執行緒呼叫起兩個子執行緒foo1和foo2並且等待他們的完成。
執行結果

2018/02/07 14:29:21 entry main
2018/02/07 14:29:21 wg.Wait()
2018/02/07 14:29:21 entry foo1
2018/02/07 14:29:21 entry foo2
2018/02/07 14:29:23 exit foo2
2018/02/07 14:29:26 exit foo1
2018/02/07 14:29:26 exit main

用法例子2:Done()過多

func main() {
    log.Println("entry main")

    var wg sync.WaitGroup
    wg.Done()

    log.Println("exit main")
}

在這個例子中,我們一上來就Done

$ go build && ./main
2018/02/07 14:33:59 entry main
panic: sync: negative WaitGroup counter

goroutine 1 [running]:
sync.(*WaitGroup).Add(0xc42006c060, 0xffffffffffffffff)
        /usr/local/go/src/sync/waitgroup.go:75 +0x134
sync.(*WaitGroup).Done(0xc42006c060)
        /usr/local/go/src/sync/waitgroup.go:100 +0x34
main.main()
        /path_to/main.go:30 +0x89

計數器小於零,panic

用法例子3:Done()過少

var wg sync.WaitGroup

func foo1() {
    log.Println("entry foo1")
    time.Sleep(5 * time.Second)
    //wg.Done()
    log.Println("exit foo1")
}


func foo2() {
    log.Println("entry foo2")
    time.Sleep(2 * time.Second)
    //wg.Done()
    log.Println("exit foo2")
}

func main() {
    log.Println("entry main")

    wg.Add(1)
    go foo1()

    wg.Add(1)
    go foo2()

    log.Println("wg.Wait()")
    wg.Wait()
    
    log.Println("exit main")
}

這個例子中我們註釋掉了兩個子執行緒中的Done()函式,執行結果:

$ go build && ./wg 
2018/02/07 14:36:03 entry main
2018/02/07 14:36:03 wg.Wait()
2018/02/07 14:36:03 entry foo2
2018/02/07 14:36:03 entry foo1
2018/02/07 14:36:05 exit foo2
2018/02/07 14:36:08 exit foo1
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x55ad7c)
        /usr/local/go/src/runtime/sema.go:56 +0x39
sync.(*WaitGroup).Wait(0x55ad70)
        /usr/local/go/src/sync/waitgroup.go:131 +0x72
main.main()
        /path_to/main.go:36 +0x127

這個錯誤表明,在最後一個活動執行緒foo1退出的時候,go檢測到當前沒有還在執行的執行緒,而還有在等待的執行緒,所以必然發生了死鎖現象,這是go的一種自我保護機制。

相關文章