本文介绍使用工作池(worker pool)模式,通过固定数量的 goroutine 和通道协作处理大量任务,避免创建过多并发导致资源耗尽,同时兼顾性能与可控性。
本文介绍使用工作池(worker pool)模式,通过固定数量的 goroutine 和通道协作处理大量任务,避免创建过多并发导致资源耗尽,同时兼顾性能与可控性。
在 Go 中批量处理海量数据(如将 10 万行文本写入数据库)时,直接为每行启动一个 goroutine(go saveToDB(line))看似简单,实则存在严重隐患:它会瞬间创建数万个 goroutine,消耗大量内存、调度开销激增,甚至触发操作系统级限制(如文件描述符耗尽、线程栈溢出),最终导致程序崩溃或性能断崖式下降。
更合理的方式是采用固定大小的工作池(Worker Pool)模型——预先启动 N 个长期运行的 worker goroutine,通过无缓冲或带缓冲的 channel 接收任务,实现并发可控、资源复用、逻辑清晰的目标。以下是推荐实现:
func main() {
data, _ := os.ReadFile("file.txt")
lines := strings.Split(string(data), "\n")
// 创建任务通道(可设缓冲提升吞吐,如 make(chan string, 1000))
jobs := make(chan string, len(lines)) // 缓冲可选,但不建议过大
var wg sync.WaitGroup
// 启动 100 个 worker(可根据 DB 连接池大小、CPU 核心数调整)
const workers = 100
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for line := range jobs { // 阻塞接收,channel 关闭后自动退出
actuallySaveToDB(line) // 实际 DB 操作(需处理错误!)
}
}()
}
// 发送所有任务
for _, line := range lines {
jobs <- line
}
close(jobs) // 关闭通道,通知所有 worker 退出循环
wg.Wait() // 等待所有 worker 完成
}关键要点说明:
- ✅ 通道关闭即退出语义:for line := range jobs 是 Go 中标准的“消费者等待+优雅退出”写法,close(jobs) 后循环自动终止,无需额外信号;
- ✅ worker 复用,零调度开销:100 个 goroutine 持续消费任务,避免反复创建销毁;
- ⚠️ 务必处理错误:actuallySaveToDB() 内应包含重试、日志、错误分类等逻辑,生产环境不可忽略;
- ⚠️ 注意 DB 连接池匹配:worker 数量建议 ≤ 数据库连接池最大空闲连接数(如 sql.DB.SetMaxIdleConns(100)),避免连接争用;
- ? 可扩展增强:如需动态限流、优先级队列或任务超时,可引入 context.Context 或第三方库(如 golang.org/x/sync/errgroup)。
总结:永远不要无节制地 go f(x);面对高并发批量任务,请坚定选择工作池模式——它用少量 goroutine + channel 协作,换来稳定、可测、易维护的并发控制能力。


















