用 WaitGroup + channel 实现批量并发任务

发布时间 - 2026-01-29 00:00:00    点击率:
WaitGroup 负责等待所有 goroutine 结束,channel 负责传递结果或协调节奏;正确做法是启动前 wg.Add(n),每个 goroutine 结尾 defer wg.Done() 后再发结果到 channel,并在所有发送完成后关闭 channel。

WaitGroup 和 channel 各自该管什么

WaitGroup 负责“等所有 goroutine 结束”,channel 负责“传结果或协调节奏”。混用时最常见错误是:用 channel 去替代 WaitGroup 的等待逻辑,比如反复 range 一个未关闭的 channel 导致死锁,或者漏掉 wg.Done() 还指望从 channel 收完就自动退出。

正确分工:wg.Add() 在启动 goroutine 前调用;每个 goroutine 结尾必须调用 wg.Done()(最好用 defer);channel 仅用于输出结果、限流或通知完成,不承担生命周期管理职责。

并发任务结果收集的典型写法

批量任务通常需要汇总返回值,这时 channel 是自然选择,但必须配合 WaitGroup 确保所有发送完成后再关闭 channel。否则消费者可能提前退出,漏收数据。

  • 启动前 wg.Add(n),n 是任务总数
  • 每个 goroutine 执行完后 defer wg.Done(),再向 channel 发送结果(如 ch )
  • 另起一个 goroutine 调用 wg.Wait(),然后 close(ch)
  • 主 goroutine 用 for range ch 安全接收,无需担心 channel 未关闭
ch := make(chan int, 10)
wg := 

&sync.WaitGroup{} for i := 0; i < 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() ch <- id * 2 }(i) } go func() { wg.Wait() close(ch) }() for res := range ch { fmt.Println(res) }

为什么不能只用 channel 不用 WaitGroup

只靠 close(ch) 并不能保证所有 goroutine 已执行完毕——你可能在某个 goroutine 还没走到 ch 那行时就关闭了 channel,导致它 panic:send on closed channel。反过来,如果不用 channel 而只用 wg.Wait(),又拿不到中间结果。

关键点在于:WaitGroup 保证“执行结束”,channel 保证“通信安全”。二者不是替代关系,而是互补关系。尤其当任务耗时差异大、部分失败需继续执行其余任务时,这种组合能避免过早关闭 channel 或遗漏结果。

容易被忽略的缓冲区和关闭时机

channel 缓冲区大小设太小会导致 goroutine 阻塞在发送端,拖慢整体进度;设太大则浪费内存。更隐蔽的问题是关闭 channel 的 goroutine 可能比所有 worker 启动得还慢,造成竞态。

  • 缓冲区建议设为任务数 cap(ch) == n,或略大(如 n+1),避免阻塞
  • close(ch) 必须在 wg.Wait() 之后,且只能由一个 goroutine 执行
  • 不要在 worker 内部判断 “我是最后一个” 然后关 channel —— 没有可靠方式识别“最后一个”
  • 如果任务可能 panic,defer wg.Done() 仍有效,但需额外 recover,否则 channel 不会关闭

实际项目里,真正难的不是写对这几行,而是想清楚:哪些数据必须按序收?哪些可以丢?失败要不要重试?这些决策会直接决定 channel 类型(带缓冲/无缓冲)、是否用带超时的 select、以及是否引入 error channel。


# go  # ai  # golang  # 并发  # channel  # 死锁  # 我是  # 自然选择  # 完成后  # 还没  # 走到  # 设为  # 并在  # 不承担  # 能在 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 网站制作企业,网站的banner和导航栏是指什么?  用yum安装MySQLdb模块的步骤方法  想要更高端的建设网站,这些原则一定要坚持!  Laravel如何优雅地处理服务层_在Laravel中使用Service层和Repository层  Laravel怎么防止CSRF攻击_Laravel CSRF保护中间件原理与实践  PHP 500报错的快速解决方法  深圳网站制作的公司有哪些,dido官方网站?  Laravel Docker环境搭建教程_Laravel Sail使用指南  php后缀怎么变mp4格式错误_修改扩展名提示格式不对怎么办【技巧】  如何用PHP快速搭建CMS系统?  Laravel怎么使用Session存储数据_Laravel会话管理与自定义驱动配置【详解】  如何在新浪SAE免费搭建个人博客?  Laravel Blade组件怎么用_Laravel可复用视图组件的创建与使用  Laravel路由怎么定义_Laravel核心路由系统完全入门指南  千问怎样用提示词获取健康建议_千问健康类提示词注意事项【指南】  php打包exe后无法访问网络共享_共享权限设置方法【教程】  如何在腾讯云服务器快速搭建个人网站?  高端云建站费用究竟需要多少预算?  Laravel用户密码怎么加密_Laravel Hash门面使用教程  浏览器如何快速切换搜索引擎_在地址栏使用不同搜索引擎【搜索】  如何用PHP工具快速搭建高效网站?  青岛网站建设如何选择本地服务器?  Swift中switch语句区间和元组模式匹配  iOS中将个别页面强制横屏其他页面竖屏  油猴 教程,油猴搜脚本为什么会网页无法显示?  如何用IIS7快速搭建并优化网站站点?  Laravel如何为API编写文档_Laravel API文档生成与维护方法  如何在服务器上配置二级域名建站?  Laravel如何升级到最新版本?(升级指南和步骤)  Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程  Laravel怎么判断请求类型_Laravel Request isMethod用法  Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理  香港服务器建站指南:外贸独立站搭建与跨境电商配置流程  如何在阿里云虚拟服务器快速搭建网站?  Laravel如何使用缓存系统提升性能_Laravel缓存驱动和应用优化方案  重庆市网站制作公司,重庆招聘网站哪个好?  Laravel Sail是什么_基于Docker的Laravel本地开发环境Sail入门  JavaScript模板引擎Template.js使用详解  Javascript中的事件循环是如何工作的_如何利用Javascript事件循环优化异步代码?  百度浏览器如何管理插件 百度浏览器插件管理方法  Laravel怎么实现微信登录_Laravel Socialite第三方登录集成  EditPlus中的正则表达式 实战(1)  儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?  Laravel如何优化应用性能?(缓存和优化命令)  如何选择可靠的免备案建站服务器?  如何在建站之星网店版论坛获取技术支持?  如何用好域名打造高点击率的自主建站?  Win11关机界面怎么改_Win11自定义关机画面设置【工具】  购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?  如何在香港免费服务器上快速搭建网站?