插件窝 干货文章 Go语言sync.WaitGroup如何正确使用才能避免并发编程中的等待问题?

Go语言sync.WaitGroup如何正确使用才能避免并发编程中的等待问题?

goroutine 调用 WaitGroup 计数器 298    来源:    2025-03-13

在Go语言中,sync.WaitGroup 是一种用于等待一组 goroutine 完成的同步机制。正确使用 sync.WaitGroup 可以避免并发编程中的等待问题,确保主 goroutine 在所有子 goroutine 完成后再继续执行。以下是正确使用 sync.WaitGroup 的步骤和注意事项:

1. 初始化 WaitGroup

首先,你需要创建一个 sync.WaitGroup 实例。

var wg sync.WaitGroup

2. 增加计数器

在启动每个 goroutine 之前,调用 wg.Add(1) 来增加 WaitGroup 的计数器。这个计数器表示你期望等待的 goroutine 数量。

wg.Add(1)

3. 启动 goroutine

在启动 goroutine 时,将 wg.Done() 放在 goroutine 的最后,以确保在 goroutine 完成时减少计数器。

go func() {
    defer wg.Done() // 确保在 goroutine 结束时调用 Done
    // 你的并发任务代码
}()

4. 等待所有 goroutine 完成

在主 goroutine 中调用 wg.Wait(),这会阻塞主 goroutine,直到所有子 goroutine 完成(即计数器变为 0)。

wg.Wait()

5. 完整示例

以下是一个完整的示例,展示了如何正确使用 sync.WaitGroup

package main

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

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1) // 增加计数器
        go func(i int) {
            defer wg.Done() // 确保在 goroutine 结束时调用 Done
            time.Sleep(time.Second * time.Duration(i))
            fmt.Printf("Goroutine %d done\n", i)
        }(i)
    }

    wg.Wait() // 等待所有 goroutine 完成
    fmt.Println("All goroutines done")
}

6. 注意事项

  • 确保 wg.Add 在 goroutine 启动之前调用:如果在 goroutine 启动之后再调用 wg.Add,可能会导致主 goroutine 在子 goroutine 完成之前就调用了 wg.Wait(),从而导致程序提前退出。

  • 确保 wg.Done 被调用:在每个 goroutine 的最后调用 wg.Done(),或者使用 defer wg.Done() 来确保即使 goroutine 发生 panic,wg.Done() 也会被调用。

  • 避免计数器为负数:如果 wg.Done() 被调用的次数超过了 wg.Add() 的次数,WaitGroup 的计数器会变为负数,这会导致 panic。

7. 总结

通过正确使用 sync.WaitGroup,你可以有效地管理并发任务,确保主 goroutine 在所有子 goroutine 完成后再继续执行。遵循上述步骤和注意事项,可以避免并发编程中的等待问题。