学习目标
- 理解并发模式在 Go 工程中的价值
- 掌握常见并发模式:worker pool、fan-out、fan-in
- 学会控制并发度,避免 goroutine 失控
- 将 goroutine、channel、select、context 组合成可控并发结构
一、为什么需要并发模式
在前面的章节中,你已经学会了:
- 如何启动 goroutine
- 如何用 channel 通信
- 如何用 select 做调度
- 如何用 context 管理生命周期
但在真实工程中,问题往往不是“能不能并发”,而是:
- 并发多少才合适?
- 如何避免 goroutine 无限制增长?
- 如何组织多个 goroutine 协作?
并发模式就是这些问题的成熟解决方案。
二、worker pool(工作池)模式
worker pool 是最常见、最重要的并发模式之一。
核心思想:
- 预先启动固定数量的 worker goroutine
- 通过 channel 分发任务
- 限制并发度,防止资源耗尽
基本结构:
func worker(id int, jobs <-chan int, results chan<- int) {
for job := range jobs {
results <- job * 2
}
}启动 worker pool:
jobs := make(chan int)
results := make(chan int)
for w := 0; w < 3; w++ {
go worker(w, jobs, results)
}说明:
- worker 数量 = 并发度上限
- jobs channel 是任务队列
三、worker pool 的工程要点
- worker 数量通常与 CPU 核数或外部资源限制相关
- jobs channel 通常由生产者关闭
- results channel 常配合 WaitGroup 或 fan-in 使用
工程认知:worker pool 的本质是“并发限流”。
四、fan-out 模式(任务分发)
fan-out 指:
将一个输入流,分发给多个 goroutine 并行处理
示例:
for i := 0; i < 3; i++ {
go func() {
for job := range jobs {
fmt.Println("process job:", job)
}
}()
}特点:
- 多个 goroutine 同时从同一个 channel 读取
- 每个任务只会被其中一个 goroutine 处理
工程场景:任务并行处理、请求并发执行。
五、fan-in 模式(结果汇聚)
fan-in 指:
将多个输入 channel 合并为一个输出 channel
示例结构:
func fanIn(cs ...<-chan int) <-chan int {
out := make(chan int)
for _, c := range cs {
go func(ch <-chan int) {
for v := range ch {
out <- v
}
}(c)
}
return out
}说明:
- fan-in 常用于汇聚多个 worker 的结果
- 通常需要配合 close 控制生命周期
六、fan-in 的关闭问题(非常重要)
一个关键问题是:
什么时候关闭 out channel?
常见做法:
- 使用 sync.WaitGroup 统计输入 channel 完成情况
- 所有输入处理完成后,再关闭 out
工程结论:
fan-in 中,关闭 channel 的责任必须非常清晰,否则极易 panic 或泄漏。
七、并发模式 + context 的组合
在现代 Go 工程中:
- worker pool 几乎一定要支持 context 取消
- 一旦请求取消,所有 worker 都应尽快退出
示例结构:
func worker(ctx context.Context, jobs <-chan int) {
for {
select {
case <-ctx.Done():
return
case job, ok := <-jobs:
if !ok {
return
}
fmt.Println("job:", job)
}
}
}工程认知:并发模式必须“可中断、可回收”。
八、常见并发模式误区
- 无限启动 goroutine,不做并发限制
- fan-out 后忘记 fan-in,导致 goroutine 泄漏
- channel 关闭职责不清晰
- 没有 context,任务无法取消
九、并发模式的工程定位总结
- worker pool:控制并发度
- fan-out:并行化处理
- fan-in:结果汇聚
- context:生命周期控制
一句话总结:
并发不是“同时做很多事”,而是“可控地同时做正确的事”。
预告:Day 17|并发安全与数据竞争(race condition)
下一天将系统讲解:
- 什么是数据竞争
- Go race detector 的使用
- 常见并发 bug 形态
- 如何写出 race-free 的 Go 代码