
本文详解 go 中 fan-in 模式下因未关闭输出通道导致的死锁问题,介绍使用 sync.waitgroup 协调多个 goroutine 完成后安全关闭通道的惯用做法,并提供可运行的修复代码与关键注意事项。
本文详解 go 中 fan-in 模式下因未关闭输出通道导致的死锁问题,介绍使用 sync.waitgroup 协调多个 goroutine 完成后安全关闭通道的惯用做法,并提供可运行的修复代码与关键注意事项。
在 Go 的并发编程中,fan-in 是一种常见模式:将多个输入通道的数据合并到一个输出通道中,供下游消费。但若未在所有输入源耗尽后显式关闭输出通道,for range 循环将永远阻塞——这正是原代码发生死锁的根本原因。
原 fanIn 函数存在两个关键缺陷:
- 未关闭 out 通道:for v := range stuff 依赖通道关闭来退出循环,而 out 始终未被关闭;
- 嵌套 goroutine 引发竞态与资源浪费:内层 go func(c int) { out
✅ 正确的惯用解法是:
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 使用 sync.WaitGroup 跟踪所有读取 goroutine 的生命周期;
- 在所有输入通道读取完毕后,在独立 goroutine 中调用 close(out),避免阻塞任何 reader;
- 直接向 out 发送值,无需中间 goroutine。
以下是修复后的完整 fanIn 实现:
import "sync" func fanIn(in ...<p>⚠️ 关键注意事项:</p>
- wg.Wait() 必须在 goroutine 中调用:若直接在主 goroutine 中等待,会阻塞 fanIn 返回,导致调用方无法开始读取;
- 缓冲大小需合理权衡:过小易造成 sender 阻塞,过大占用内存;生产环境建议结合 select + default 或 context 实现超时/取消;
- 避免“goroutine 泄漏”:确保每个 wg.Add(1) 都有对应 wg.Done(),此处由 defer 保障;
- 不可重复关闭通道:close(out) 仅执行一次,sync.WaitGroup 天然保证这一点。
该方案符合 Go 并发哲学——“不要通过共享内存来通信,而应通过通信来共享内存”,清晰分离了数据流(channel)与控制流(WaitGroup),是构建健壮管道(pipeline)的基础模式。










