前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang sync.WaitGroup 简介与用法

Golang sync.WaitGroup 简介与用法

作者头像
恋喵大鲤鱼
发布2019-08-01 11:00:45
1.6K0
发布2019-08-01 11:00:45
举报
文章被收录于专栏:C/C++基础C/C++基础

1.简介

sync.WaitGroup 用于阻塞等待一组 Go 程的结束。主 Go 程调用 Add() 来设置等待的 Go 程数,然后该组中的每个 Go 程都需要在运行结束时调用 Done(), 递减 WaitGroup 的 Go 程计数器 counter。当 counter 变为 0 时,主 Go 程被唤醒继续执行。

代码语言:javascript
复制
type WaitGroup struct {
    // contains filtered or unexported fields
}

//设置需要等待的 Go 程数量
func (wg *WaitGroup) Add(delta int)

//Go 程计数器减 1
func (wg *WaitGroup) Done()

//阻塞等待所有 Go 程结束(等待 Go 程计数器变为 0)
func (wg *WaitGroup) Wait()

WaitGroup 有三个方法,其中 Done() 调用了 Add(-1)。标准用法: (1)启动 Go 程时调用 Add(); (2)在 Go 程结束时调用 Done(); (3)最后调用 Wait()。

2.使用示例

代码语言:javascript
复制
package main

import (
    "fmt"
    "sync"    
    "time"
)

var wg sync.WaitGroup
	
func foo1() {
    defer wg.Done()
    fmt.Println("entry foo1")
    time.Sleep(2 * time.Second)
    fmt.Println("exit foo1")
}


func foo2() {
    defer wg.Done()
    fmt.Println("entry foo2")
    time.Sleep(4 * time.Second)
    fmt.Println("exit foo2")
}

func main() {
    fmt.Println("entry main")
	
    wg.Add(2)
    go foo1()
    go foo2()

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

编译运行输出:

代码语言:javascript
复制
entry main
wg.Wait()
entry foo2
entry foo1
exit foo1
exit foo2
exit main

3.错误示例

如果使用过程中通过 Add()添加的 Go 程数与调用 Done() 的次数不符,即 sync.WaitGroup 的 Go 程计数器等所有子 Go 程结束后不为 0,则会引发 panic。

3.1 Done()过多

代码语言:javascript
复制
func main() {
    fmt.Println("entry main")

    var wg sync.WaitGroup
    wg.Done()

    fmt.Println("wg.Wait()")
    wg.Wait()

    fmt.Println("exit main")
}

编译运行输出:

代码语言:javascript
复制
entry main
panic: sync: negative WaitGroup counter

goroutine 1 [running]:
sync.(*WaitGroup).Add(0xc4200140d0, 0xffffffffffffffff)
	/usr/lib/golang/src/sync/waitgroup.go:75 +0x134
sync.(*WaitGroup).Done(0xc4200140d0)
	/usr/lib/golang/src/sync/waitgroup.go:100 +0x34
main.main()
	/data/goTest/src/waitgroup/main.go:34 +0x8e

可见,当 Go 程计数器变为负数时,将引发 panic。

3.2 Done() 过少

注释掉 foo2() 中的 defer wg.Done(),使得 Go 程结束时不减少 sync.WaitGroup 的 Go 程计数器。

代码语言:javascript
复制
func foo2() {
    //defer wg.Done()
    fmt.Println("entry foo2")
    time.Sleep(4 * time.Second)
    fmt.Println("exit foo2")
}

编译运行输出:

代码语言:javascript
复制
entry main
wg.Wait()
entry foo2
entry foo1
exit foo1
exit foo2
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x54aa7c)
	/usr/lib/golang/src/runtime/sema.go:56 +0x39
sync.(*WaitGroup).Wait(0x54aa70)
	/usr/lib/golang/src/sync/waitgroup.go:131 +0x72
main.main()
	/data/goTest/src/waitgroup/main.go:33 +0x10e

这个错误表明,在最后一个活动线程 foo2 退出的时候,Go 检测到当前没有还在运行的 Go 程,但主 Go 程仍在等待,发生了死锁现象,于是引发 panic,这是 Go 的一种自我保护机制。


参考文献

[1] Golang [2] golang语言异步通信之WaitGroup

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年07月12日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.简介
  • 2.使用示例
  • 3.错误示例
    • 3.1 Done()过多
      • 3.2 Done() 过少
      • 参考文献
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档