无缓冲channel要求发送与接收双方同时就绪,否则阻塞;若stage未用goroutine启动,上游写入即死锁,触发“all goroutines are asleep - deadlock”。

为什么直接链式调用 pipeline 会死锁
写 square(gen(2, 3)) 看似简洁,实际是同步阻塞调用:上游 gen 启动 goroutine 往 channel 写,但下游 square 还没启动任何 goroutine 去读,out 永久卡住,最终触发 <code>fatal error: all goroutines are asleep - deadlock。
- 每个 stage 必须显式用
go func() { ... }()启动,不能靠函数返回值“自动连接” -
gen返回的是,它只是个“发射器”,没人监听就等于没开枪 - 无缓冲 channel 要求发送和接收双方“同时就绪”,缺一不可
怎么安全关闭 channel 避免 panic 或漏数据
在 stage goroutine 里写 defer close(out) 很危险——如果上游提前关闭 in,stage 提前 return,defer 仍会执行,导致下游 range 立刻退出,而上游可能还在往已关闭的 out 发送,触发 panic: send on closed channel。
- 关闭动作必须放在所有
out 操作之后、goroutine 退出前,且不能依赖 <code>defer - 用
for n := range in安全读取,它隐式处理ok == false并自然退出 - 中间 stage 只负责关自己的
out;最下游(如printresults)不关任何 channel - 过滤型 stage(如丢弃空行)必须确保:输入读完 + 所有有效输出发完,再
close(out)
为什么不该盲目加 sync.Pool 或大缓冲 channel
看到“高吞吐”就加 sync.Pool 缓存结构体,或设 make(chan T, 1000),反而让问题更隐蔽:前者增加 GC 压力,后者掩盖背压缺失,内存悄悄涨到 OOM 才暴露。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
sync.Pool在典型流式 pipeline 中基本无效——每个 item 生命周期短、类型固定、分配频次未必高 - 调试阶段一律用无缓冲 channel(
make(chan T)),让阻塞立刻显现,倒逼理清数据节奏 - 上线后加缓冲,只在明确上下游速率匹配、且延迟敏感(如实时日志采样)时才设,大小建议从 16 或 32 起步
- 有界缓冲 ≠ 背压控制:真正背压靠下游消费速度反向约束上游,不是靠 buffer 吞掉压力
context 和错误怎么插进 pipeline 不卡死
没 context 的 pipeline 无法响应超时或取消;原生 channel 不带错误语义,靠 panic 或 recover 传递错误会跨 goroutine 失效,且掩盖真实崩溃点。
- 每个 stage 函数签名必须带
ctx context.Context参数 - 所有
in和out操作都必须套select,且case 放第一顺位 - 推荐用统一结构体
type Result struct { Value T; Err error }走单 channel,比双 channel 更易管控 - HTTP 上报 stage 若用
http.Do,必须用ctx.WithTimeout包裹 client,否则单个失败请求拖垮整条 pipeline
真正的难点不在怎么连 channel,而在每个 stage 的退出路径是否干净、关闭时机是否精确、错误信号能否穿透到底——这些地方稍一松懈,跑着跑着就 goroutine 泄漏、内存堆积、数据丢失。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










