ZBLOG

sync.WaitGroup

sync.WaitGroup 详解

sync.WaitGroup 是 Go 标准库 sync 包提供的一种并发控制机制,用于等待一组 Goroutine 完成执行。它通过计数器管理协程的同步,适用于 “主 Goroutine 等待多个子 Goroutine 全部结束” 的场景。


1. 核心方法

方法 作用
Add(int) 增加等待的 Goroutine 数量(计数器 +n)。
Done() 标记一个 Goroutine 完成(计数器 -1)。
Wait() 阻塞主 Goroutine,直到计数器归零。

2. 基本使用示例

package main

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

func worker(id int, wg *sync.WaitGroup) {
	defer wg.Done() // Goroutine结束时调用Done()

	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 <= 3; i++ {
		wg.Add(1) // 每启动一个Goroutine,计数器+1
		go worker(i, &wg)
	}

	wg.Wait() // 阻塞直到所有Goroutine完成
	fmt.Println("All workers completed")
}

输出:

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

3. Key Points

(1) Add() vs Done()

  • Add(n)
    通常在启动 Goroutine 调用,表示新增 n个待完成任务。
  • Done()
    等价于 Add(-1),应在每个 Goroutine 退出时调用(通常用 defer确保执行)。

(2) Wait()

  • 阻塞当前 Goroutine,直到计数器归零。
  • 必须与 Add/Done配对使用,否则可能死锁或提前退出。

(3) WaitGroup的值传递问题

WaitGroup为结构体类型,需通过指针传递以避免复制:

// ❌错误:值传递会导致死锁!
go worker(i, wg)

// ✅正确:传递指针
go worker(i, &wg)

4. Common Pitfalls (常见陷阱)

(1) Add在Goroutine内部调用

// ❌错误:可能导致主线程先执行Wait()
go func() {
    wg.Add(1)
    defer wg.Done()
}()
wg.Wait()

修正:

// ✅正确:Add在外部调用
wg.Add(1)
go func() {
    defer wg.Done()
}()
wg.Wait()

(2) Done未调用导致死锁

// ❌错误:少调用了Done(),Wait会永久阻塞!
wg.Add(2)
go func() { /*...*/ }() // Missing Done()
wg.Wait()

(3) Add传入负数导致panic

// ❌错误:计数器不能为负!
wg.Add(-5) // panic: negative WaitGroup counter!

5. Advanced Usage (高级用法)

(1) Dynamic Task Addition (动态添加任务)

可在运行时调整计数:

var wg sync.WaitGroup

for _, task := range tasks {
	if needsParallel(task) {
		wg.Add(1)
		go processTask(task, &wg)
	}
}
wg.Wait()

(2) Combined with Channels (与Channel结合)

实现结果收集:

results := make(chan int)
var wg sync.WaitGroup

for i :=0; i <10; i++{
    wg.Add(1)
    go func(x int){
        defer wg.Done()
        results <- x*x 
    }(i)
}

//关闭channel避免泄漏(所有goroutines完成后)
go func(){
    wg.wait()
    close(results)
}()

for res:=range results{
    fmt.Println(res)
}

6. Performance Considerations (性能考量)

  • WaitGroup基于原子操作实现,轻量高效。
  • Avoid频繁创建/销毁(可复用对象)。

7. Alternatives (替代方案)

场景 推荐工具
需要超时控制 context.Context + Waitgroup
一对多通知 close(channel)
复杂依赖关系 errgroup.Group

总结来说: ✅ Use for simple “wait-for-all-goroutines” scenarios. 🚫 Avoid misuse by ensuring every Add has a matching Done!


内容由零声教学AI助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://golang.0voice.com/?id=24029

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?