用 worker pool 替代裸写 go f() 是最直接有效的优化,因高频创建 goroutine 的真实瓶颈是 runtime.newproc 调度注册、栈分配和 GC 扫描压力叠加,而非单个协程的 2KB 栈开销。

用 worker pool 替代裸写 go f() 是最直接有效的优化
高频创建 goroutine 的真实瓶颈,从来不是单个协程的 2KB 栈开销,而是 runtime.newproc 调度注册、栈内存分配、GC 扫描压力三者叠加——尤其在每秒调用上万次 go process(item) 的 HTTP handler 或消息解析场景中,pprof 很容易看到 runtime.malg 占比飙升、GC pause 明显拉长。
- 别在 for 循环里直接
go process(item):若items有 10 万条,就启 10 万个 goroutine,几乎必然拖垮调度器 - 改用固定数量的长期 worker:推荐数量为
2 * runtime.NumCPU(),这是压测验证过的较稳起点 - 任务 channel 必须带缓冲:设为 worker 数的 2–4 倍(如 8 个 worker,channel 缓冲设
make(chan func(), 32)),太小 sender 阻塞,太大浪费内存 - worker 异常退出必须处理:for-select 中 panic 会导致 goroutine 消失,需加 recover + 日志告警,或用
ants这类成熟池库自动兜底
别让 sync.Pool 成为“幻觉”,它只缓存对象,不缓存 goroutine
sync.Pool 不能复用 goroutine 本身,但能显著降低其附带的堆分配压力——比如每个请求都 new 一个 bytes.Buffer 或 JSON 解析器,不用 Pool 就是纯 GC 灾难。
-
Pool.Get()不保证返回非 nil:必须检查并 fallback 初始化,例如b := bufPool.Get().(*bytes.Buffer); if b == nil { b = &bytes.Buffer{} } -
Pool.Put()前必须重置状态:比如b.Reset()再bufPool.Put(b),否则下次Get()可能拿到残留数据 - 不适合存含指针/敏感字段的对象:比如带
http.Request引用的结构体,Put 前没清空会引发数据污染或泄漏 - 高频短任务(如日志格式化)最适合:此时对象生命周期与 goroutine 高度对齐,复用率高,GC 减少明显
同步能搞定的,就别启动 goroutine
Go 的设计哲学是“goroutine 很便宜”,但这个“便宜”是有前提的:任务本身得真需要并发。很多场景下,同步执行反而更快——省掉了调度切换、channel 通信、栈分配三重开销。
- 本地文件读小量内容、计算哈希、拼接字符串等微秒级操作,直接同步跑,
go反而增加延迟 - 循环中防止单次耗时过长?用
runtime.Gosched()让出 CPU 即可,不创建新 goroutine - 周期性任务(如健康上报)用
time.Ticker+select+done chan控制启停,避免“一启永不关” - 闭包捕获大变量(如整个
struct{ huge []byte })会触发逃逸到堆,间接放大 goroutine 内存开销,传参时只传必要字段
第三方池库 ants 能省事,但别当黑盒用
ants 是目前最成熟的 goroutine 池实现,支持动态扩容、超时回收、任务拒绝策略,适合快速落地。但它不是银弹——配置不当照样翻车。
立即学习“go语言免费学习笔记(深入)”;
- 默认
ants.NewPool(10)是固定大小池,若突发流量远超 10,新任务会阻塞或被拒绝,需根据 QPS 和 P99 响应时间调优 - 启用
ants.WithExpiryDuration(time.Second * 60)后,空闲 goroutine 会被回收,但频繁创建销毁仍存在,不如固定池稳定 - 不要把 long-running 任务(如 WebSocket 连接维持)丢进 ants 池:池设计初衷是短任务,长任务会卡住 worker,导致后续任务饿死
- 和自研 worker pool 最大区别在于:ants 把任务提交和执行解耦得更彻底,但调试时看不到底层 channel 和 worker 状态,出问题要靠它的
RunningWorkers()和Cap()指标定位
真正难的不是选哪种方案,而是判断「这个任务到底值不值得扔进 goroutine」——它可能只是个 strings.ReplaceAll,也可能藏着一次远程调用。性能优化的第一步,永远是打开 pprof 看清楚 runtime.newproc 和 runtime.malg 到底在哪涨起来的。











