sync.WaitGroup用于等待多个goroutine完成,通过Add增计数、Done减计数、Wait阻塞等待归零;示例中启动3个worker并发执行并同步等待结束。

在Golang中,sync.WaitGroup 是用于等待一组并发的goroutine完成执行的常用同步原语。它特别适用于你启动多个协程并希望主线程阻塞直到它们全部结束的场景。使用 WaitGroup 可以避免程序过早退出,也能确保所有任务都处理完毕。
WaitGroup 基本用法
WaitGroup 内部维护一个计数器,调用 Add(n) 增加计数,每个任务完成后调用 Done() 减少计数,而 Wait() 会阻塞当前协程,直到计数器归零。
基本使用步骤如下:
- 在启动 goroutine 前调用 wg.Add(1),表示新增一个待完成任务
- 每个 goroutine 执行完后调用 wg.Done(),相当于 Add(-1)
- 在主协程中调用 wg.Wait() 等待所有任务完成
简单示例:并发打印数字
下面是一个使用 WaitGroup 控制三个并发任务的例子:
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.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) // 每次启动一个协程就加1
go worker(i, &wg)
}
wg.Wait() // 阻塞直到所有 Done 被调用
fmt.Println("All workers finished")
}
输出结果类似:
Worker 1 starting Worker 2 starting Worker 3 starting Worker 1 done Worker 2 done Worker 3 done All workers finished
常见注意事项
使用 WaitGroup 时需注意以下几点,避免出现死锁或 panic:
- 确保 Add 的次数与 Done 的次数匹配:如果 Add 多了,Wait 会永远阻塞;Add 少了可能导致 panic
- 不要复制 WaitGroup:传递给 goroutine 时应传指针
- Add 应在 Go 语句前调用:若在 goroutine 内部 Add,可能因调度问题导致 Wait 提前结束
- 可以在循环中使用 defer wg.Done() 确保即使发生 panic 也能通知完成
实际应用场景
WaitGroup 常用于以下场景:
- 批量发起 HTTP 请求,并等待所有响应返回
- 并行处理文件或数据库记录
- 启动多个服务协程并在主程序退出前等待它们关闭
例如,批量请求数据:
urls := []string{"http://example.com", "http://google.com", "http://github.com"}
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
resp, err := http.Get(u)
if err != nil {
fmt.Printf("Error: %s\n", err)
return
}
fmt.Printf("Status from %s: %s\n", u, resp.Status)
resp.Body.Close()
}(url)
}
wg.Wait()
fmt.Println("All requests completed")
基本上就这些。WaitGroup 是轻量且高效的并发控制工具,掌握它的正确用法对编写可靠的并发程序至关重要。只要注意调用时机和计数匹配,就能安全地协调多个 goroutine。不复杂但容易忽略细节。










