go工作池需用chan、goroutine和sync.waitgroup手动搭建,核心在于控制并发数、正确关闭channel、传waitgroup指针并前置add调用,避免内存暴涨与调度崩溃。

Go 工作池不是“配置”出来的,而是用 chan、goroutine 和 sync.WaitGroup 搭出来的结构——没有 config 文件、没有依赖注入、不靠第三方库。初学者最容易卡在 channel 关闭时机和 worker 生命周期上,而不是语法不会写。
为什么不能直接 go process(task) 一次性起一堆 goroutine
这是最常踩的坑:看到并发就想“每个任务开一个 goroutine”,结果压测时内存飙到 2GB+,调度器卡死,runtime: failed to create new OS thread 频繁报错。
- Go 调度器对成千上万个 goroutine 的公平性不保证,大量 goroutine 竞争
runtime.mheap会导致 STW 时间变长 - HTTP 客户端默认连接池有限,瞬间几万请求会触发
net/http: request canceled (Client.Timeout exceeded while awaiting headers) - 没加限流的代码上线后,DB 连接数打满、Redis QPS 爆表,问题不是 Go 不行,是没控并发
jobs 和 results channel 该用带缓冲还是无缓冲
带缓冲是生产环境的默认选择,无缓冲只适合极简 demo 或调试逻辑。
-
jobs := make(chan int, 100):缓冲大小 ≈ 平均单次突发任务量,防主 goroutine 因 worker 暂时忙而阻塞 -
results := make(chan Result, 100):同样要缓冲,否则 worker 处理完任务后卡在results ,整个 pool 停摆 - 用
make(chan int, 0)(即无缓冲)必须确保 worker 已启动且 ready,否则jobs 会永久阻塞 - 缓冲太大会吃内存,比如
make(chan Task, 10000)存的是指针,但若 Task 结构体含大字段(如[]byte),实际内存占用远超预期
worker 函数里必须加 defer func() { recover() }() 吗
必须加,而且要放在 for job := range jobs 循环外层,不是 inside。
- 一个 worker panic 会导致它退出,
range自动终止,但其他 worker 不受影响;问题是wg.Done()没执行,wg.Wait()永远等不到它 - recover 放错位置也没用:放在循环里只能 recover 当前 task 的 panic,worker 还是会退出;必须包住整个函数体
- 示例写法:
func worker(id int, jobs
关闭 jobs 和 results 的顺序与时机
顺序错了就死锁,时机错了就漏结果或 panic。
- 必须先
close(jobs),再wg.Wait(),最后close(results)—— 三步缺一不可 - 不能在投任务的 goroutine 里 close(jobs),否则可能还有任务没发完就关了;正确做法是另起 goroutine 投完再关
-
results只能在所有 worker 全部退出后 close,否则主 goroutinefor r := range results会 panic:“send on closed channel” - 如果不用
range results,而是用for i := 0; i ,那 <code>close(results)可省略,但得确保接收次数精准匹配
真正难的不是写出来,是让工作池在 panic、超时、任务量突增、worker 挂掉一半这些真实场景下不卡、不丢、不 panic。channel 关闭逻辑和 recover 位置,多写两遍就熟了;但 buffer size 和 worker 数量,得靠压测调,没法靠猜。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











