goroutine 并非万能加速器,盲目滥用会因调度开销、文件描述符耗尽等拖慢接口;真正提速需满足可并行、无强依赖、高 I/O 占比;须用 WaitGroup 或 errgroup.Group 正确同步与错误传播,并严格管控超时、取消与资源清理。

goroutine 不是万能加速器,盲目加反而拖慢接口
Go 的 goroutine 确实轻量,但每个仍需调度、栈内存和上下文切换开销。HTTP 接口里直接对每个请求启动几十个 goroutine,尤其在高并发下容易压垮 runtime 调度器或耗尽文件描述符。真正提速的前提是:任务可并行、无强依赖、I/O 占比高(如调用多个外部 API、查不同 DB 表、读多个缓存 key)。
用 sync.WaitGroup 控制并发任务生命周期
常见错误是启动 goroutine 后不等结果就返回,导致数据丢失或 panic;或者用 time.Sleep 硬等,完全失去并发意义。必须显式等待所有子任务完成。
-
WaitGroup是最直接的同步方式,适合已知任务数量、无需传递中间结果的场景 - 记得在
goroutine内部调用wg.Done(),且必须在 defer 中或逻辑末尾确保执行 - 不要在循环中直接传循环变量(如
for _, u := range users { go func() { fmt.Println(u.Name) }() }),会闭包捕获同一地址 —— 改为传参:go func(user User) { ... }(u)
func handleUserBatch(users []User) []UserInfo {
var wg sync.WaitGroup
results := make([]UserInfo, len(users))
for i, u := range users {
wg.Add(1)
go func(idx int, user User) {
defer wg.Done()
results[idx] = fetchUserInfo(user.ID) // 假设是耗时 HTTP 或 DB 查询
}(i, u)
}
wg.Wait()
return results
}
优先考虑 errgroup.Group 处理带错误传播的并发
当多个子任务中任意一个失败需中断全部(比如批量创建资源,一个失败则整体回滚),sync.WaitGroup 就不够用了。errgroup.Group 自动支持取消、错误收集和上下文传播,是更健壮的选择。
- 导入:
"golang.org/x/sync/errgroup" - 用
eg.Go(func() error { ... })启动任务,第一个非 nil 错误会触发其余任务取消 - 调用
eg.Wait()阻塞直到全部完成或出错,返回首个错误 - 注意:如果任务本身不响应
ctx.Done(),取消可能不生效 —— 所有 I/O 操作(如http.Client.Do、db.QueryContext)必须传入上下文
func batchProcess(ctx context.Context, items []string) error {
g, ctx := errgroup.WithContext(ctx)
results := make([]string, len(items))
for i, item := range items {
i, item := i, item // 防止闭包捕获
g.Go(func() error {
res, err := callExternalAPI(ctx, item) // 必须用 ctx
if err != nil {
return err
}
results[i] = res
return nil
})
}
if err := g.Wait(); err != nil {
return err
}
// use results...
return nil
}
别忽略 goroutine 泄漏和上下文超时配置
接口响应时间受最慢子任务拖累。没设超时的 HTTP 请求、死锁 channel、未关闭的数据库连接,都会让 goroutine 挂起不退出,长期积累导致内存泄漏甚至 OOM。
立即学习“go语言免费学习笔记(深入)”;
- 所有外部调用必须绑定上下文,例如
http.NewRequestWithContext(ctx, ...)、db.QueryRowContext(ctx, ...) - 给整个并发组设总超时:
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second),并在函数结束调用cancel() - 避免用
select {}或空for{}无限阻塞;channel 操作要配默认分支或超时 - 用
go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=2定期检查异常增长的 goroutine 数量
真正卡点往往不在“怎么起 goroutine”,而在“怎么安全收尾”——超时、取消、错误聚合、资源清理,每一步漏掉都可能让优化变成负优化。











