美文网首页Go语言用例Go知识库go
golang语言异步通信之WaitGroup

golang语言异步通信之WaitGroup

作者: CodingCode | 来源:发表于2018-02-07 14:57 被阅读50次

    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的一种自我保护机制。

    相关文章

      网友评论

        本文标题:golang语言异步通信之WaitGroup

        本文链接:https://www.haomeiwen.com/subject/nioozxtx.html