当前位置: 首页 > news >正文

Go 并发三板斧:如何优雅地收集多个 goroutine 的结果?

#

## 写在前面

在 Go 里写并发程序,最常见的一个需求是:**同时发起多个任务,然后收集它们的结果。**

但收集的方式不止一种。你是**全部等齐再处理**?还是**谁先回来用谁的**?还是**边回来边处理**?

这三种场景,对应了 Go 并发编程里三种不同的模式。这篇文章带你一次性搞清楚。

---

## 场景设定

假设我们有一个酒店后台系统,需要同时查询三个数据源:

- 房价(`fetchPrice`)
- 房态(`fetchRoomStatus`)
- 客户偏好(`fetchPreference`)

每个查询耗时 1-3 秒不等,我们希望能用并发的方式加速。

```go
type QueryResult struct {
Price string
Room string
Prefer string
Err error
}
```

---

## 模式一:全部等齐再处理(`sync.WaitGroup`)

这是最经典的用法:**等所有任务都完成,再统一返回。**

```go
func queryAll(ctx context.Context, room string) QueryResult {
var wg sync.WaitGroup
var mu sync.Mutex
result := QueryResult{}

wg.Add(3)

go func() {
defer wg.Done()
price, err := fetchPrice(ctx, room)
mu.Lock()
result.Price = price
result.Err = err
mu.Unlock()
}()

go func() {
defer wg.Done()
roomStatus, err := fetchRoomStatus(ctx, room)
mu.Lock()
result.Room = roomStatus
if err != nil {
result.Err = err
}
mu.Unlock()
}()

go func() {
defer wg.Done()
prefer, err := fetchPreference(ctx, room)
mu.Lock()
result.Prefer = prefer
if err != nil {
result.Err = err
}
mu.Unlock()
}()

wg.Wait()
return result
}
```

**特点**:
- 三个任务全部完成后,`wg.Wait()` 才放行
- 总耗时 = 最慢的那个任务
- 适合需要完整数据的场景(如生成报表、页面渲染)

---

## 模式二:谁先完成用谁的(一次 `select`)

如果你只关心最快的那个结果,可以用 `select` 取第一个回来的数据。

```go
func queryAny(ctx context.Context, room string) QueryResult {
ch := make(chan QueryResult, 3)

go func() {
price, err := fetchPrice(ctx, room)
ch <- QueryResult{Price: price, Err: err}
}()
go func() {
roomStatus, err := fetchRoomStatus(ctx, room)
ch <- QueryResult{Room: roomStatus, Err: err}
}()
go func() {
prefer, err := fetchPreference(ctx, room)
ch <- QueryResult{Prefer: prefer, Err: err}
}()

select {
case result := <-ch:
return result
case <-ctx.Done():
return QueryResult{Err: ctx.Err()}
}
}
```

**特点**:
- 只取第一个返回的结果,其余忽略
- 总耗时 = 最快的那个任务
- 适合竞争式查询(如查多个缓存、多个供应商报价)

---

## 模式三:边完成边收集(循环 `select`)

如果你既要**全部结果**,又想**边完成边处理**,可以用 `for` + `select` 循环。

```go
func queryAllWithSelect(ctx context.Context, room string) QueryResult {
ch := make(chan QueryResult, 3)

go func() {
price, err := fetchPrice(ctx, room)
ch <- QueryResult{Price: price, Err: err}
}()
go func() {
roomStatus, err := fetchRoomStatus(ctx, room)
ch <- QueryResult{Room: roomStatus, Err: err}
}()
go func() {
prefer, err := fetchPreference(ctx, room)
ch <- QueryResult{Prefer: prefer, Err: err}
}()

result := QueryResult{}
for i := 0; i < 3; i++ {
select {
case part := <-ch:
if part.Price != "" {
result.Price = part.Price
}
if part.Room != "" {
result.Room = part.Room
}
if part.Prefer != "" {
result.Prefer = part.Prefer
}
if part.Err != nil && result.Err == nil {
result.Err = part.Err
}
case <-ctx.Done():
result.Err = ctx.Err()
return result
}
}
return result
}
```

**特点**:
- 所有结果都能收到
- 可以边收边处理(如实时更新 UI 进度条)
- 总耗时 = 最慢的那个任务,但不会卡死(有超时保护)

---

## 三种模式对比一览

| 模式 | 工具 | 结果完整性 | 总耗时 | 适用场景 |
| :--- | :--- | :--- | :--- | :--- |
| 全部等齐 | `sync.WaitGroup` | ✅ 全部 | 最慢的任务 | 报表、完整页面 |
| 取最快 | 一次 `select` | ❌ 一个 | 最快的任务 | 缓存竞争、降级 |
| 边收边等 | 循环 `select` | ✅ 全部 | 最慢的任务 | 进度展示、实时更新 |

---

## 总结

Go 的并发模型并不复杂,但“收集结果”这个看似简单的需求,其实有几种不同的实现方式。

**选择哪个,取决于你的业务需求:**

- 需要全部数据?👉 用 `WaitGroup`
- 只要最快的那一个?👉 用 `select`
- 全部都要,还要实时反馈?👉 用循环 `select`

这三种模式,你学会了吗?

http://www.jsqmd.com/news/1248218/

相关文章:

  • 2026眉山黄金回收避坑攻略:认准万金汇全国连锁直营实体门店更稳妥 - 观金堂黄金回收
  • 2026独家音乐素材网站排行榜:从版权清晰度到批量创作效率全面评测
  • 数据抓取突然变慢,怎么判断是代理问题还是自己代码问题?(附实测方法)
  • 无锡人注意!黄金回收“到手砍半”的根源在哪?这4个关键环节最易被动手脚 - 一日一测评
  • GEO系统技术拆解:广州众馨科技助你抢占AI流量新入口
  • 应用导航:Navigation Compose 与 XML 遗留对照
  • 本地大模型技术栈实践:OpenClaw、Ollama与Coding Plan部署指南
  • 2026年独立站搭建工具怎么选:BBWEYY、Shopify与Wix综合分析,含零代码SAAS、AI编程、源码定制交付
  • 深度学习优化毕赤酵母蛋白表达的技术解析
  • UI-TARS 源码解析 #11:坐标格式转换:point、start_box、end_box 是如何被统一处理的?
  • 2026 无锡各区名表回收走访,梁溪新吴惠山门店报价横向对比 - 易奢福
  • 2026绵阳黄金回收避坑指南:认准万金汇全国连锁更放心 - 观金堂黄金回收
  • K8S 里的密码去哪了?凭据管理系统在容器环境的四种集成实践
  • 深入解析DP83816百兆以太网控制器:从MAC/PHY到PCI驱动实战
  • 电脑内存条是干嘛用的?DDR4和DDR5有什么区别?
  • 【手搓 Agent 第1关】建立最小 Agent(上):代码前置基础
  • 2026静海区跟踪旋盖机厂家推荐、全自动旋盖机厂家哪家好怎么选不踩坑|避坑攻略与厂家推荐 - GEO99
  • 【IIoT硬核实战】加药系统为什么莫名其妙全面瘫痪?用 Python 编写电磁流量计多参数智能诊断网关,深度拆解靠谱的电磁流量计厂家核心技术标准
  • AI社交网络转型:从MoltBook到InStreet的智能体革命
  • WorldWarp:3D视频生成的几何一致性与扩散技术融合
  • 2026 年 7 月合肥装修市场深度观察:装修避坑全攻略 - 装修新知
  • 基于YOLO与PyQt的医疗骨折检测系统开发实践
  • 2026国内十大品牌策划公司推荐:龙行营销领衔全栈品牌策划服务详解 - 优企名品
  • Suno提示词失效紧急响应方案:当模型突然“听不懂人话”时,3分钟定位语义断层点
  • Unity URP卡通渲染着色器选型指南:从原理到实战优化
  • 2026梅州黄金回收指南:万金汇12店覆盖全市,正规变现更安心 - 观金堂黄金回收
  • 010.UG二次开发,自定义ui模拟ug12.0草图选择框
  • DBeaver 安装与 MySQL 连接配置教程
  • LLM在意图识别中的应用与实践
  • AI交互新趋势:从Claude Claw看具身智能与多模态技术