不能直接用 for range 启动任务函数,因为裸调 go f() 会失控执行顺序、错误传播和生命周期,导致乱序完成、panic 后继续运行、超时泄漏;必须用 context.Context 封装,任务函数签名强制为 func(context.Context) error,并在 HTTP/DB 调用中透传 ctx,errCh 需设为带缓冲 1 的 channel,worker 需 defer 检查 ctx.Err(),concurrency 指 worker pool 大小而非并发数。

为什么不能直接用 for range 启动任务函数
因为裸调 go f() 会立刻失去对执行顺序、错误传播和生命周期的控制。常见现象包括:任务乱序完成、某个 f() panic 后其余仍继续跑、超时后 goroutine 泄漏无法回收。这些不是“并发没写好”,而是缺少上下文与状态封装——就像教孩子走路,光给腿不给平衡感,迟早摔。
context.Context 必须出现在每个任务签名里
不是可选,是强制。所有任务函数必须定义为 func(context.Context) error,否则无法响应取消、无法传递超时、无法跨阶段携带元数据(如 traceID、重试次数)。实际中容易忽略两点:
- 在任务内部调用 HTTP 或 DB 操作时,必须把传入的
ctx显式传给http.NewRequestWithContext或db.QueryContext,否则超时无效 - 若任务需重试,应在
ctx.Err() != nil时立即返回,而不是等重试完才检查
错误通道 errCh 要设为带缓冲的 1 容量
这是 fail-fast 的物理保障。设成无缓冲或容量 >1,会导致第一个错误被阻塞或掩盖后续错误;设成 0 容量则可能因没人读而 panic。正确做法是:
- 声明为
errCh := make(chan error, 1) - 只在主调度逻辑中 select 一次,收到首个非 nil error 就调用
cancel()并关闭所有输出 channel - 每个 worker goroutine 在 defer 中检查
ctx.Err(),避免误发错误到已关闭的errCh
并行度 concurrency 不是“开多少 goroutine”,而是 worker pool 大小
设 concurrency = 3,不代表同时跑 3 个任务,而是最多有 3 个活跃 worker 在消费任务队列。它影响的是资源水位,不是语义顺序。容易踩的坑:
立即学习“go语言免费学习笔记(深入)”;
- 误以为设高 concurrency 就能加速——实际瓶颈常在 I/O 或下游服务,盲目提高只会压垮数据库连接池
- 设为 0 或负数未做校验,导致
for i := 0; i 循环不执行,整个流水线静默卡死 - 串行场景下设
concurrency = 1是合理选择,它保留了 context 取消和统一错误通道能力,比手写 for 循环更健壮
真正难的不是把任务丢进 goroutine,而是让它们在出错时能一起收手、在超时时能一起退场、在中间某步失败时不让后续步骤白忙活——这些都得靠 context、channel 和显式状态管理来锚定,而不是靠“多开几个协程”来堆砌。


















