必须显式关闭channel,否则for range永不退出;生成器阶段需在发完数据后close(out),处理器阶段消费完输入再关输出,消费者只读不关,靠range自动感知关闭。

流水线必须显式关闭 channel,否则下游 for range 永不退出
很多人写完 generator 函数后直接返回 out channel,却忘了在 goroutine 内部调用 close(out)。结果下游用 for range ch 一直阻塞,程序卡死——这不是 bug,是 channel 语义决定的:for range 只在 channel 关闭且缓冲为空时才退出。
- 生成器阶段(Source)必须在发送完所有数据后执行
close(out) - 处理器阶段(Transform)应在消费完输入 channel 后,再关闭自己的输出 channel
- 消费者阶段(Sink)只读不关,靠
for range自动感知上游关闭 - 若某阶段可能提前退出(如 context 被 cancel),也要主动 close 输出 channel,避免下游永久等待
goroutine 生命周期必须同步,WaitGroup 和 done channel 不可少
常见错误是启动一堆 goroutine 后直接 return,main 函数退出导致所有子 goroutine 被强制终止——position0 打印不出来,不是逻辑错,是没等它跑完。
- 每个启动的 goroutine 都要配对
wg.Add(1)和defer wg.Done() - 主协程最后必须调用
wg.Wait(),否则无法保证阶段执行完成 - 若需业务级信号(比如“所有数据已落库”),额外加
done chan struct{},由最下游 goroutine 关闭并通知 - WaitGroup 控制生命周期,done channel 传递业务完成信号,二者不互斥,常共存
缓冲大小不是越大越好,IO/CPU 密集型场景策略不同
用 make(chan int, 1024) 看似“更稳”,实则掩盖背压、拖慢响应、甚至引发 OOM。缓冲不是保险丝,是流量调节阀。
- IO 密集型(HTTP 请求、DB 查询):建议小缓冲,
16 ~ 128。让生产者稍作等待,避免下游积压超时 - CPU 密集型(图像缩放、加密解密):可适度加大,
512 ~ 2048,减少调度开销,但绝不能用math.MaxInt - 无缓冲 channel(
make(chan int))适合强顺序/低吞吐控制,但极易因上下游节奏不匹配而阻塞 - 缓冲值应随压测调整,而非凭经验拍定
错误和 context 必须独立传递,绝不混进数据 channel
把 error 塞进 chan int 或用 chan interface{} 统一收发,等于放弃类型安全和错误处理契约。真实流水线失败是常态,不是例外。
立即学习“go语言免费学习笔记(深入)”;
- 每个阶段函数签名应包含
ctx context.Context参数,支持超时与取消传播 - 错误走单独 channel:
errCh chan error,或返回结构体struct { data T; err error } - 消费者端统一收集
errCh,按需决定跳过、重试或终止整条流水线 - 切勿在 goroutine 内部
panic,也不忽略ctx.Err(),否则会泄漏 goroutine


















