一架梯子,一头程序猿,仰望星空!

golang WaitGroup和Once


本章介绍go语言sync包中的两个同步原语:

  • WaitGroup - 主要用于等待一组并发的任务
  • Once - 用于在并发环境中仅执行一次的任务

1. WaitGroup

WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。

基本用法说明:

// WaitGroup内部维护了一个计数器,用于统计需要等待的任务数量,当计数器的值等于0的时候,Wait函数就会直接返回,否则阻塞。
var wg sync.WaitGroup

// 内部计数加2
wg.Add(2)

// 内部计数减1
wg.Done()


// 如果内部计数器大于0,则阻塞,计数器等于0则返回。
wg.Wait()

例子:

// 初始化WaitGroup对象
var wg sync.WaitGroup

// url数组
var urls = []string{
    "http://www.golang.org/",
    "http://www.google.com/",
    "http://www.somestupidname.com/",
}

// 访问所有的url
for _, url := range urls {
    // WaitGroup的计数加1
    wg.Add(1)
    
    // 使用协程访问URL
    go func(url string) {
        // 调用WaitGroup的Done函数,实际上就是内部计数减1
        defer wg.Done()
        
        // 访问url
        http.Get(url)
    }(url)
}

// 调用WaitGroup的Wati函数等待所有url加载完成。
// 因为有3个url需要访问,Add函数调用3次,计数器等于3,
// url访问完成后,调用Done函数3次,计数器就会等于0
wg.Wait()

2. Once

Once是只执行一次动作的对象。

例子:

// 定义Once对象
var once sync.Once

onceBody := func() {
    fmt.Println("我只跑一次。")
}

done := make(chan bool)

for i := 0; i < 10; i++ {
    // 并发执行10次
    go func() {
        // 调用Once对象的Do函数执行onceBody函数
        // 注意:一个Once对象只能执行一次,无论调用多少次Do函数。
        once.Do(onceBody)
        
        done <- true
    }()
}
for i := 0; i < 10; i++ {
    <-done
}

输出:

我只跑一次。