Go 用 errgroup 管理并发子任务:错误收敛、取消传播与限流
Go 用 errgroup 管理并发子任务:错误收敛、取消传播与限流并发拉一批数据,你大概率写过这样的代码:开五个 goroutine 各自请求一个接口,用sync.WaitGroup等它们全部结束。跑起来没问题,直到有一天某个接口挂了——其他四个还在傻等,错误也没人收,日志里只留下一条超时。更糟的是,第一个失败之后剩下的请求已经没意义了,却还在白白消耗连接和 CPU。golang.org/x/sync/errgroup就是为解决这类问题而生的:它在 WaitGroup 的基础上,帮你收敛第一个错误、并在出错时自动取消其他子任务。这篇把它的三个实战用法讲透:错误收敛、取消传播、并发限流。先看朴素写法的问题用原生 WaitGroup 并发请求,想拿到「任意一个失败就返回错误」并不容易:funcfetchAll(urls[]string)error{varwg sync.WaitGroupvarmu sync.MutexvarfirstErrerrorfor_,u:rangeurls{wg.Add(1)gofunc(ustring){deferwg.Done()iferr:fetch(u);err!nil{mu.Lock()iffirstErrnil{// 只记第一个错误,得手动加锁firstErrerr}mu.Unlock()}}(u)}wg.Wait()returnfirstErr}问题一眼看不完:要手动加锁保护firstErr、要手动Add/Done、而且某个请求失败后其余请求不会停,continue 跑到底。这些样板代码每写一次都容易漏一个wg.Done()导致死锁。用 errgroup 收敛错误同样的逻辑,errgroup 一把梭:importgolang.org/x/sync/errgroupfuncfetchAll(urls[]string)error{varg errgroup.Groupfor_,u:rangeurls{u:u// 捕获循环变量(Go 1.22 前必须,之后可省)g.Go(func()error{returnfetch(u)// 直接返回 error,g 帮你收第一个})}returng.Wait()// 任意一个非 nil,Wait 就返回它}g.Go接收一个func() error,内部帮你管理 WaitGroup;g.Wait()会阻塞到所有子任务结束,并返回第一个非 nil 的错误。锁、计数器全省了。注意语义:Wait只返回第一个错误,后面的错误会被丢弃。如果你需要所有错误,得自己在闭包里收集(比如塞进一个带锁的 slice),errgroup 不替你做这件事。关键升级:出错就取消其他任务上面的版本虽然收了错,但失败后其余请求仍会跑完。真正的价值在errgroup.WithContext:它派生一个 context,任意子任务返回非 nil 错误时,自动 cancel 这个 context,其他子任务只要监听了 ctx 就能及时退出。funcfetchAll(ctx context.Context,urls[]string)([]string,error){g,ctx:errgroup.WithContext(ctx)// 派生可取消的 ctxresults:make([]string,len(urls))fori,u:rangeurls{i,u:i,u g.Go(func()error{// 把 ctx 透传给下游,失败时这里会随 ctx 一起被取消body,err:fetchWithCtx(ctx,u)iferr!nil{returnerr// 触发 ctx cancel,其他 goroutine 收到 Done}results[i]body// 各写各的下标,无需加锁returnnil})}iferr:g.Wait();err!nil{returnnil,err}returnresults,nil}funcfetchWithCtx(ctx context.Context,urlstring)(string,error){req,_:http.NewRequestWithContext(ctx,http.MethodGet,url,nil)resp,err:http.DefaultClient.Do(req)// ctx 被 cancel 时这里立刻返回 erroriferr!nil{return,err}deferresp.Body.Close()b,err:io.ReadAll(resp.Body)returnstring(b),err}这里有个容易忽略的细节:results[i] body每个 goroutine 写不同下标,读写不重叠,所以不需要加锁。这是并发写 slice 安全的少数场景之一——前提是长度预分配好、各 goroutine 下标互不相同。要让取消真正生效,子任务里的阻塞操作必须监听 ctx。HTTP 用NewRequestWithContext,数据库用QueryContext,自己的循环用select { case -ctx.Done(): return ctx.Err() ... }。如果下游不吃 ctx,cancel 就是一纸空文,任务照样跑到底。用 SetLimit 限流,别一次开几千个 goroutine如果 urls 有几千个,上面的写法会瞬间开几千个 goroutine 和连接,把下游打挂。Go 1.20 起 errgroup 内置了SetLimit,限制同时运行的子任务数:funcfetchAll(ctx context.Context,urls[]string)error{g,ctx:errgroup.WithContext(ctx)g.SetLimit(10)// 最多 10 个 goroutine 并发for_,u:rangeurls{u:u g.Go(func()error{// 超过 10 个时,g.Go 会阻塞直到有空位returnfetchWithCtx2(ctx,u)})}returng.Wait()}SetLimit(n)之后,g.Go在并发数达到上限时会阻塞,直到有子任务结束腾出名额。这等价于自己维护一个容量为 n 的信号量 channel,但代码干净得多。注意SetLimit必须在任何g.Go之前调用,中途改会 panic。如果你不想让g.Go阻塞,而是希望「没名额就跳过」,可以用TryGo,它返回 bool 表示是否成功启动。一个真实的坑:漏传 ctx 导致取消失效最常见的翻车是这样:用了WithContext拿到新 ctx,却在子任务里图省事用了外层的旧 ctx 或context.Background()。g,ctx:errgroup.WithContext(parentCtx)g.Go(func()error{// 错误:用了 parentCtx,errgroup 的 cancel 传不进来returnfetchWithCtx(parentCtx,u)})errgroup.WithContext返回的新ctx 才是挂了 cancel 钩子的那个。子任务必须用这个新 ctx,取消才会传播。记住口诀:g, ctx : errgroup.WithContext(...)之后,组内一律用左边这个新ctx。小结并发子任务要「收敛第一个错误」,别再手写 WaitGroup 锁,用errgroup.Group。需要「一个失败就停掉其他」时用errgroup.WithContext,并把返回的新 ctx透传给每个子任务的阻塞调用。任务数很大时用SetLimit(n)限流,避免 goroutine 与连接爆炸;要非阻塞就用TryGo。各 goroutine 写不同 slice 下标可以免锁,但共享变量仍要自己保护。一句话记忆点:errgroup WaitGroup 第一个错误 自动取消 限流,但取消能不能生效,全看你有没有把新 ctx 传下去。

相关新闻

深度探索Java字节码:5个实战技巧助你成为JVM专家

深度探索Java字节码:5个实战技巧助你成为JVM专家

深度探索Java字节码:5个实战技巧助你成为JVM专家 【免费下载链接】Krakatau Java decompiler, assembler, and disassembler 项目地址: https://gitcode.com/gh_mirrors/kr/Krakatau Krakatau是一款功能强大的Java字节码工具集,为开发者提供了直接…

2026/9/21 17:25:00 阅读更多 →
独立LLM应用开发:基于GPT-5.6-Sol的/goal功能完整实现指南

独立LLM应用开发:基于GPT-5.6-Sol的/goal功能完整实现指南

在构建独立LLM应用的过程中,如何让模型理解并执行复杂任务一直是开发者面临的挑战。DAIR.AI的Elvis Saravia提出的/goal功能设计,结合GPT-5.6-Sol模型的能力,为这一难题提供了创新解决方案。本文将完整拆解/goal功能的实现原理、环境搭建、代…

2026/9/20 23:22:50 阅读更多 →
OpenCore Legacy Patcher完整指南:让旧Mac焕发新生,轻松升级最新macOS

OpenCore Legacy Patcher完整指南:让旧Mac焕发新生,轻松升级最新macOS

OpenCore Legacy Patcher完整指南:让旧Mac焕发新生,轻松升级最新macOS 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 还在为你的老款…

2026/9/19 21:29:49 阅读更多 →

最新新闻

下载小红书避坑指南:3步搞定环境配置,带你入门到精通

下载小红书避坑指南:3步搞定环境配置,带你入门到精通

下载小红书避坑指南:3步搞定环境配置,带你入门到精通 配置环境就卡半天?别急,这不仅是你的痛点,也是无数开发者从入门到精通路上最真实的绊脚石。很多新人拿到《下载小红书》这类涉及数据抓取或API对接的面试题时,第一反应是去网上找现成的代码,结…

2026/9/22 19:50:45 阅读更多 →
智能抄表系统面试必问:3分钟吃透核心逻辑

智能抄表系统面试必问:3分钟吃透核心逻辑

智能抄表系统面试必问:3分钟吃透核心逻辑 面试被问原理答不上来?别慌,今天把智能抄表系统核心逻辑拆透。很多候选人背了八股文,一追问数据怎么从电表传到云端就卡壳。 这其实是 面试必问…

2026/9/22 19:50:45 阅读更多 →
阿波罗汽车自动驾驶栈配置避坑指南一文搞懂

阿波罗汽车自动驾驶栈配置避坑指南一文搞懂

阿波罗汽车自动驾驶栈配置避坑指南一文搞懂 配置环境就卡半天,是不是你的常态?很多刚接触阿波罗(Apollo)自动驾驶仿真与开发的朋友,一打开终端敲下 source 或者编译代码,屏幕就开始疯狂滚动日志,最后报出一堆 dependency…

2026/9/22 19:50:45 阅读更多 →
塞尔达血月多久一次保姆级教程:3分钟搞定配置不再卡半天

塞尔达血月多久一次保姆级教程:3分钟搞定配置不再卡半天

塞尔达血月多久一次保姆级教程:3分钟搞定配置不再卡半天 配置环境就卡半天?别慌,这坑我替大家踩过了。今天这篇保姆级教程,专门解决你因为“塞尔达血月多久一次”这种看似游戏机制,实则是前端数据驱动与状态管理难题而导致的开发阻塞。很多转岗前端的朋…

2026/9/22 19:50:45 阅读更多 →
豆角英文翻译避坑指南:搞定环境配置卡半天的终极方案

豆角英文翻译避坑指南:搞定环境配置卡半天的终极方案

豆角英文翻译避坑指南:搞定环境配置卡半天的终极方案 配置环境就卡半天?这大概是很多开发者在尝试处理【豆角英文】相关数据或进行国际化(i18n)开发时最真实的痛点。你以为只是查个单词,结果一跑代码,依赖冲突、编码乱码、时区错误接踵而至。这篇【…

2026/9/22 19:49:45 阅读更多 →
词林底层逻辑拆解:3个核心步骤帮新手避坑,告别教程依赖症

词林底层逻辑拆解:3个核心步骤帮新手避坑,告别教程依赖症

词林底层逻辑拆解:3个核心步骤帮新手避坑,告别教程依赖症 看了一堆教程还是不会写项目,这是绝大多数编程新手的噩梦。你跟着视频敲代码能跑通,自己换个需求就抓瞎,这种“手残心不残”的状态,往往是因为没搞懂底层的【词林】机制。今天不讲虚的,咱们直…

2026/9/22 19:49:45 阅读更多 →

日新闻

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天

3台商务办公笔记本实测:手写实现环境配置,告别卡半天 配置环境就卡半天?别怪机器慢,多半是你没选对工具链。在Java、Go或Python的项目现场, 手写实现…

2026/9/22 0:00:41 阅读更多 →
剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑

剑帝加点速查手册:3分钟搞懂核心逻辑 面试被问原理答不上来,是不是常态?别慌。很多开发者对着 GitHub 开源仓库里的代码发呆,看似简单实则暗藏玄机。今天这份【剑帝加点】速查手册,直接带你拆解核心实现,把面试必考的原理讲透。…

2026/9/22 0:00:41 阅读更多 →
手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优

手写实现图片压缩网站核心:搞定WebP转换与质量调优 复制来的代码跑不通不知道怎么调?别慌,这种“复制粘贴地狱”在开发圈太常见了。尤其是做 图片压缩网站…

2026/9/22 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/22 4:32:41 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/22 4:38:57 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/22 8:51:04 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/22 2:43:42 阅读更多 →