直接用 go func(){}() 易崩,因 goroutine 非免费(2KB/个)、频繁启停致内存与调度开销大,且易打爆下游连接数(如 MySQL 默认151);需合理限流,ants 等库封装了 WaitGroup、chan、panic 捕获等逻辑。

为什么直接用 go func() {}() 容易崩
协程不是免费的——每个 goroutine 至少占用 2KB 栈空间,频繁启停会触发大量内存分配和调度开销。更关键的是,没节制地起协程去调 API、读文件或连数据库,很容易打爆下游服务或本地连接数限制,比如 MySQL 默认最大连接数才 151,go http.Get() 一并发几百个,http: server closed idle connection 或 dial tcp: too many open files 就来了。
所以不是“能不能用协程”,而是“谁来管它、怎么限、限多少”。原生 sync.WaitGroup + chan 可以手写控制,但容易漏关 channel、忘记 recover panic、统计不准确;而 ants 这类库就是把这套逻辑封装稳了。
ants.NewPool() 的三个关键参数怎么选
初始化池子时最常卡在 ants.NewPool(100) 这一行:数字填多少?填小了压不住并发,填大了等于没限。实际得看场景:
- IO 密集型(HTTP 请求、DB 查询):通常设为后端服务的吞吐瓶颈值,比如目标 API 的 QPS 上限是 200,单次请求平均耗时 200ms,理论并发 ≈ 200 × 0.2 = 40,再加点余量,填
50比较稳妥 - CPU 密集型(加密、压缩、图像处理):别硬堆数量,一般不超过
runtime.NumCPU(),否则线程切换开销反超收益 - 混合型或不确定场景:先从
10起步,用 pprof 看goroutines和schedule latency曲线,逐步上调
另外两个参数:ants.WithNonblocking(true) 让 Submit() 不阻塞(任务进不去就丢弃),适合日志采集这类可容忍丢失的场景;ants.WithExpiryDuration(10 * time.Second) 控制空闲 worker 存活时间,避免长周期服务里积攒一堆僵尸 goroutine。
立即学习“go语言免费学习笔记(深入)”;
submit 任务时 panic 会吃掉错误吗
会,而且默认不吃日志——这是 ants 最容易被骂的点。你传进去一个会 panic 的函数:pool.Submit(func() { panic("boom") }),程序不会崩,但你啥也看不到。
必须主动接管 panic 处理:
- 全局设置:
ants.WithPanicHandler(func(p interface{}) { log.Printf("ants panic: %v", p) }) - 或者在任务内部自己
recover,尤其当任务来自不可信输入(比如用户上传的脚本逻辑)时,别依赖池子兜底 - 注意:如果用了
WithNonblocking,提交失败(比如池满)也不会返回 error,得靠回调或监控指标发现
还有一个坑:Submit 接收的是 func(),不能直接传带参函数,常见写法是闭包捕获变量,但要注意循环中变量复用问题——for i := range tasks { pool.Submit(func() { fmt.Println(i) }) } 最终可能全打出最后一个 i 值。
不用 ants,原生 semaphore + WaitGroup 能行吗
能,而且更轻、更可控,适合简单固定并发上限的场景。核心就两行:
var sem = make(chan struct{}, 10) // 并发数上限
wg.Add(len(tasks))
for _, task := range tasks {
go func(t Task) {
sem <- struct{}{} // 获取信号量
defer func() { <-sem }() // 释放
defer wg.Done()
t.Run()
}(task)
}但缺点也很明显:
- 没任务队列,超限时直接阻塞 goroutine,可能拖慢整个流程
- 没运行中 worker 统计,没法做动态扩缩容
- 没 panic 捕获、没超时控制、没优雅关闭支持(
pool.Release()会等所有任务结束,sem方案得自己协调)
所以如果你只做一次性批处理、并发数恒定、也不关心监控和容错,原生方案干净利落;但只要涉及长期运行的服务、需要可观测性、或下游稳定性不可控,ants 的那层封装就不是“多此一举”,而是省掉后续三天 debug 的成本。
真正难的从来不是选哪个库,而是想清楚:你的并发瓶颈到底在哪儿——是网卡?是数据库连接池?还是 GC 压力?限流只是表象,背后得对系统链路有真实观测数据支撑。



















