Golang中的sync.WaitGroup是什么?

参考回答

sync.WaitGroup 是 Go 的 sync 包中提供的一个工具,用于管理和等待一组 goroutine 完成。它通过计数器的机制跟踪 goroutine 的数量,并提供一种简单的方式确保主程序在所有 goroutine 完成之前不会退出。

sync.WaitGroup 的核心方法:
1. Add(delta int):将等待计数器加 delta,通常在启动 goroutine 前调用。
2. Done():将等待计数器减 1,通常在 goroutine 完成时调用。
3. Wait():阻塞主程序,直到计数器归零。


示例代码

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // goroutine 完成时调用 Done()

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

    // 模拟工作
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    // 启动多个 goroutine
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 启动一个 goroutine,计数器加 1
        go worker(i, &wg)
    }

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

输出

Worker 1 starting
Worker 3 starting
Worker 2 starting
Worker 1 done
Worker 3 done
Worker 2 done
All workers finished

详细讲解与拓展

1. WaitGroup 的工作原理

  • WaitGroup 使用一个内部计数器来记录需要等待的 goroutine 数量。
  • 调用 Add(n) 将计数器加 n,表示将等待 n 个 goroutine。
  • 调用 Done() 将计数器减 1,表示一个 goroutine 完成了。
  • 调用 Wait() 阻塞当前 goroutine,直到计数器归零,表示所有 goroutine 都完成了。

2. 注意事项

  1. Add 必须在启动 goroutine 之前调用
    如果 Addgo worker() 之后调用,可能会导致计数器为负,导致运行时错误。

    wg.Add(1) // 正确:在 goroutine 启动前调用 Add
    go worker(&wg)
    
  2. 不要重复使用已完成的 WaitGroup
    一个 WaitGroupWait() 返回后,不能再次使用,否则会导致未定义的行为。

  3. 确保 Done()Add() 配对
    每次 Add() 的数量必须与 Done() 的调用次数一致,否则会导致死锁或计数器不匹配。


3. 实际应用场景

  1. 等待多个 goroutine 完成
    适用于需要协调多个 goroutine 并确保所有任务完成后再继续执行的场景。

    示例:

    func main() {
       var wg sync.WaitGroup
    
       for i := 0; i < 5; i++ {
           wg.Add(1)
           go func(id int) {
               defer wg.Done()
               fmt.Printf("Goroutine %d working\n", id)
           }(i)
       }
    
       wg.Wait()
       fmt.Println("All goroutines done")
    }
    
  2. 限流和并发控制
    配合其他工具(如 channel),可以实现对 goroutine 数量的限制。

    示例:

    func main() {
       var wg sync.WaitGroup
       semaphore := make(chan struct{}, 3) // 最多允许 3 个 goroutine 并发
    
       for i := 0; i < 10; i++ {
           wg.Add(1)
           go func(id int) {
               defer wg.Done()
               semaphore <- struct{}{}        // 占用一个资源
               fmt.Printf("Goroutine %d running\n", id)
               <-semaphore                    // 释放资源
           }(i)
       }
    
       wg.Wait()
       fmt.Println("All goroutines done")
    }
    

4. sync.WaitGroup 的优势

  • 简单易用:通过计数器机制,轻松管理一组 goroutine。
  • 线程安全:可以在多个 goroutine 中同时调用其方法。
  • 灵活扩展:适用于并发场景的多种需求,例如等待、限流等。

总结

  1. 核心方法
    • Add(delta int):增加等待计数器。
    • Done():减少等待计数器。
    • Wait():阻塞,直到计数器为 0。
  2. 使用场景
    • 等待一组 goroutine 完成。
    • 配合其他工具(如 channel)实现更复杂的并发控制。
  3. 注意事项
    • Add 必须在启动 goroutine 之前调用。
    • 不要重复使用已完成的 WaitGroup

通过熟练掌握 sync.WaitGroup,可以轻松管理并发任务,提升代码的健壮性和可维护性。

发表评论

后才能评论