当前位置:首页 > 文章列表 > Golang > Go问答 > WaitGroup Go 方法调用顺序的并发收尾

WaitGroup Go 方法调用顺序的并发收尾

来源:17golang原创 2026-10-11 01:17:28 0浏览 收藏

使用 sync.WaitGroup 做并发收尾时,最稳的顺序是:先在计数还是空的时候启动任务,再等待计数归零。Go 1.25 起可以用 WaitGroup.Go 把“增加计数、启动 goroutine、任务结束后减少计数”收进一个调用;如果继续使用传统写法,则必须把正数 Add 放在创建 goroutine 之前。

官方文档:https://pkg.go.dev/sync#WaitGroup

一句话记忆:空的 WaitGroup 上,Go 或正数 Add 要先于 Wait;一轮任务结束后,下一轮才可以重新添加任务。

先确定任务启动与 Wait 的边界

可以把并发收尾拆成三个边界:主 goroutine 负责建立任务计数,任务函数负责在正常返回时完成收尾,Wait 只负责等待计数归零。这样看,Wait 不是“启动任务”的方法,也不会替调用方补上尚未建立的计数。

WaitGroup、WaitGroup.Go、任务计数与 Done 的静态启动收尾关系说明图
图1:WaitGroup 启动与收尾边界说明图,展示 Go、任务计数、Done 和 Wait 的职责关系;这是结构说明图,不是运行截图。

因此,下面的最小形式是清楚的:

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup

	// Go 负责增加任务计数并启动函数;空计数时它必须先于 Wait。
	wg.Go(func() {
		// 任务正常返回后,WaitGroup 会自动完成收尾。
		fmt.Println("worker finished")
	})

	// Wait 只等待计数归零,不负责创建任务。
	wg.Wait()
	fmt.Println("all tasks finished")
}

WaitGroup.Go 的函数不能 panic;如果任务可能把错误作为结果返回,应把错误写入受控的结果通道或错误收集器,而不是依赖 panic 传播。任务内部还可以继续调用同一个 WaitGroup 的 Go,但空计数时仍然要确保外层先建立任务,再进入 Wait。

传统 Add/Done 写法要把计数放在 goroutine 外

兼容旧版本 Go 或需要显式控制任务数量时,可以使用 Add、Done 和 Wait。真正关键的不是方法名称,而是正数 Add 和 Wait 的先后关系:当计数为零时,正数 Add 必须发生在 Wait 之前,通常也要发生在创建 goroutine 之前。

package main

import (
	"fmt"
	"sync"
)

func runBatch(items []string) {
	var wg sync.WaitGroup
	wg.Add(len(items)) // 先建立完整计数,避免 Wait 看到空计数。

	for _, item := range items {
		item := item // 为每个 goroutine 固定本轮循环值。
		go func() {
			defer wg.Done() // 无论函数从哪个正常路径返回,都减少一次计数。
			fmt.Println("processing", item)
		}()
	}

	wg.Wait() // 只有计数归零后才继续收尾。
}

把 wg.Add(1) 写进新 goroutine 是常见错误:主 goroutine 可能先执行 Wait,此时计数仍为零,Wait 会直接返回,任务计数就和实际任务脱节。需要动态增加任务时,也要保证增加发生在当前 WaitGroup 已经有任务、或者上一轮 Wait 已经返回的安全边界内。

复用 WaitGroup 时不要跨轮添加任务

WaitGroup 可以用于多轮独立任务,但上一轮的 Wait 尚未返回时,不要开始下一轮的正数 Add。将它理解为一张一次只服务一个任务集合的收尾账单即可:先记账,再消费,归零并收口后才能开下一张账。

WaitGroup Add、Done、Wait 与分轮复用边界的静态关系说明图
图2:WaitGroup 分轮复用契约说明图,展示上一轮 Wait 返回后才能进入下一轮 Add/Go;这是结构说明图,不是运行截图。
func processRounds(rounds [][]string) {
	var wg sync.WaitGroup

	for _, round := range rounds {
		wg.Add(len(round)) // 本轮开始前登记本轮任务。
		for _, name := range round {
			name := name
			go func() {
				defer wg.Done() // 当前任务完成后归还一个计数。
				process(name)
			}()
		}
		wg.Wait() // 本轮完全收口后,才进入下一轮。
	}
}

如果不需要按轮等待,而是想让任务持续追加,可以让 WaitGroup 在计数大于零时继续接收新的 Go;但最终调用 Wait 的位置仍要由拥有任务集合的那一层决定。不要复制已经使用过的 WaitGroup,也不要把它当作可传递的业务结果容器。

并发收尾的四个落地检查点

检查项正确边界常见后果
空计数启动Go 或正数 Add 先于 WaitWait 提前返回
任务收尾传统写法在 goroutine 内用 defer Done计数无法归零或出现负数 panic
分轮复用上一轮 Wait 返回后再进入下一轮 Add/Go任务集合边界不清
对象所有权WaitGroup 首次使用后不复制并发状态被拆成两份

最后再补一个版本选择:Go 1.25 及以上、任务函数不会 panic 时,WaitGroup.Go 更容易把顺序写对;需要兼容旧版本或显式登记一批任务时,Add/Done/Wait 仍然合适。无论采用哪种接口,都应把“谁负责启动、谁负责收尾、哪一层负责等待”写在同一个局部结构里。

常见问题

为什么不能在 goroutine 内先 Add 再 Wait?

因为新 goroutine 何时真正运行没有保证,主 goroutine 可能先执行 Wait。如果此刻计数还是零,Wait 会直接返回。

WaitGroup.Go 能不能替代错误处理?

不能。它只管理任务计数和等待关系,不会替你聚合错误;错误应通过结果通道、受控的共享结构或其他明确的取消/收集方案传回调用方。

把方法顺序固定下来后,WaitGroup 的排查就不再是“偶发并发问题”:先看空计数时是否先启动,接着看每个任务是否完成收尾,最后看下一轮是否等到了上一轮的 Wait 返回。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
科研实验室采购耗材时的批号与验收记录科研实验室采购耗材时的批号与验收记录
上一篇
科研实验室采购耗材时的批号与验收记录
松石色冰洞与折射光带手机壁纸提示词
下一篇
松石色冰洞与折射光带手机壁纸提示词
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    409次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    487次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    495次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    443次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    271次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码