当前位置: 技术文章>> 100道Go语言面试题之-Go语言中的sync.WaitGroup是如何实现等待一组goroutine完成的?

文章标题:100道Go语言面试题之-Go语言中的sync.WaitGroup是如何实现等待一组goroutine完成的?
  • 文章分类: 后端
  • 9771 阅读

在Go语言中,sync.WaitGroup 是一个非常有用的同步原语,用于等待一组 goroutine 的完成。它内部使用计数器来跟踪活动的 goroutine 数量。每个 goroutine 在开始执行时调用 Add 方法来增加计数器的值,并在完成执行时调用 Done 方法来减少计数器的值。当计数器的值变为 0 时,所有在 WaitGroup 上等待的 goroutine(通过调用 Wait 方法)都会被唤醒并继续执行。

实现机制

  1. 计数器(Counter)sync.WaitGroup 内部维护一个计数器,用于跟踪需要等待的 goroutine 数量。

  2. Add 方法Add(delta int) 方法用于增加或减少计数器的值。当 delta 为正数时,增加计数器的值;当 delta 为负数时,如果减少后的值小于 0,则会引发 panic。这确保了 WaitGroup 不会被错误地减少到负数。

  3. Done 方法Done() 方法是 Add(-1) 的便捷封装,用于在 goroutine 完成时减少计数器的值。

  4. Wait 方法Wait() 方法会阻塞调用它的 goroutine,直到计数器的值变为 0。这意呀着所有通过 Add 方法增加的 goroutine 都已经通过调用 Done 方法完成了它们的任务。

使用示例

package main

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

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 确保 goroutine 结束时减少计数器

    fmt.Printf("Worker %d starting\n", id)

    time.Sleep(time.Second) // 模拟耗时操作
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1) // 增加计数器
        go worker(i, &wg)
    }

    wg.Wait() // 等待所有 worker 完成
    fmt.Println("All workers have finished")
}

在这个例子中,我们启动了 5 个 goroutine 来模拟并行工作。每个 goroutine 在开始时通过 wg.Add(1) 增加计数器,并在结束时通过 defer wg.Done()(即 defer wg.Add(-1))减少计数器。main 函数中的 wg.Wait() 调用会阻塞,直到所有 worker goroutine 都通过调用 Done 方法完成了它们的任务,此时计数器的值变为 0,Wait 方法返回,程序继续执行。