Skip to content

并发

sync.WaitGroup : 等待一组goroutine执行完毕; 三个主要方法:

  • Add(delta int) : 设置等待的goroutine数量;
  • Done() : 通知WaitGroup一个goroutine已经完成;
  • Wait() : 等待所有goroutine完成;

使用示例

go
func processItems(items []int) {
    var wg sync.WaitGroup
    
    // 添加要处理的items数量
    wg.Add(len(items))
    
    results := make([]int, len(items))
    
    for i, item := range items {
        // 启动处理每个item的goroutine
        go func(i, item int) {
            defer wg.Done()
            
            // 处理item
            results[i] = item * item
            fmt.Printf("Processed item %d: %d\n", i, results[i])
        }(i, item) // 避免出现闭包的问题
    }
    
    // 等待所有处理完成
    wg.Wait()
    
    // 处理结果
    sum := 0
    for _, v := range results {
        sum += v
    }
    fmt.Printf("Sum of processed items: %d\n", sum)
}

常见陷阱和最佳实践

  1. Add 和 Done 必须平衡 确保每次调用 Add(n) 最终都对应 n 次 Done() 调用,否则程序可能: 永久阻塞:如果 Done() 调用次数少于 Add(n) 中的 n 崩溃:如果 Done() 调用次数多于 Add(n) 中的 n(计数器为负时触发 panic)

  2. Add 的调用时机

go
// 错误方式:在goroutine内部调用Add
for i := 0; i < 10; i++ {
    go func() {
        wg.Add(1)  // 错误!可能还没有调用Add就已经调用了Wait
        defer wg.Done()
        // 工作...
    }()
}
wg.Wait()  // 可能过早返回

// 正确方式:在启动goroutine前调用Add
for i := 0; i < 10; i++ {
    wg.Add(1)
    go func() {
        defer wg.Done()
        // 工作...
    }()
}
wg.Wait()
  1. 不要复制 WaitGroup WaitGroup 是一个结构体,复制它并尝试管理同一个 WaitGroup 的多个副本会导致不确定行为。
go
// 错误方式
func worker(wg sync.WaitGroup) {  // wg被复制
    defer wg.Done()  // 这个Done操作的是复制的wg
    // 工作...
}

// 正确方式
func worker(wg *sync.WaitGroup) {  // 传递指针
    defer wg.Done()
    // 工作...
}
本站访客数 人次 本站总访问量