Go中无内置goroutine池,需用固定数量worker+带缓冲channel实现任务复用;缓冲大小应≈峰值积压量×1.5,关闭时须先关channel再WaitGroup等待执行完成,并为任务添加context支持取消。

Go 里没有内置的 goroutine 池,所谓“池”,就是固定数量的 goroutine + 带缓冲的 chan 实现任务复用——不是复用协程本身(它不能复用),而是避免每个任务都新建一个。
为什么必须用带缓冲的 tasks channel
无缓冲 chan 是同步点:Submit 会卡住,直到有空闲 worker 取走任务。HTTP handler 里直接调 pool.Submit(),高并发下极易拖垮整个请求链路。
- 缓冲大小 ≠ worker 数量,而应 ≈ 单次峰值积压量 × 1.5(例如每秒最多涌进 200 个任务、平均处理耗时 50ms,缓冲设 300 更稳)
- 过小(如
make(chan Task, 1))等于没缓冲,退化成同步调用 - 过大(如
make(chan Task, 10000))会让任务滞留内存,GC 回收延迟,还掩盖了消费者吞吐不足的问题
如何安全关闭池并等待所有任务完成
直接 close(p.tasks) 不等于所有任务已执行完——worker 可能刚取出任务、还没执行完,range 就退出了。
- 必须用
sync.WaitGroup或errgroup.Group跟踪实际执行完成 - worker 内部要
defer wg.Done(),且wg.Add(1)必须在go worker()前调用 - 关闭顺序不能错:先停生产者(关闭
taskschan),再wg.Wait(),最后才释放资源 - 别在
Close()里close(p.tasks)后就返回——调用方会误以为“已清空”,实际任务还在飞
要不要给任务加 context.Context
不加,就是“甩出去不管”;加了,才能支持超时、取消、透传 deadline——这对 I/O 密集型任务(HTTP 请求、DB 查询)几乎是刚需。
立即学习“go语言免费学习笔记(深入)”;
- 原始
type Task func()无法响应取消;应升级为type Task func(context.Context) error - worker 中必须用
select监听ctx.Done(),并在收到时立即退出,避免任务“幽灵执行” - 不要把
context.WithTimeout放在Submit侧统一包一层——那样所有任务共享同一个 deadline,不合理;应在每个任务创建时按需设置 - 纯计算任务加
context开销略增,但换来可观察、可中断的能力,值得
缓冲区大小和关闭时机是两个最常被忽略的点,尤其在 HTTP handler 或定时任务中直接 Submit 的场景,一不留神就会阻塞或漏任务。


















