什么是协程池
协程池简单理解就是有一个池子一样的东西,里面装这个固定数量的goroutine,当有一个任务到来的时候,会将这个任务交给池子里的一个空闲的goroutine去处理,如果池子里没有空闲的goroutine了,任务就会阻塞等待。

如何构建协程池
首先定义一个协程池的结构体
go
type WorkerPool struct {
Jobs chan func()//公共的通道,到时候任务写进这里面
MaxWorkers int//设置协程池最多有多少个协程
}
然后创建一个协程
go
func NewWorkerPool(maxWorkers int) *WorkerPool {
pool := &WorkerPool{
Jobs: make(chan func()),//初始化
MaxWorkers: maxWorkers,
}
for i := 0; i < pool.MaxWorkers; i++ {
go pool.worker()//这里就是创建多个协程
}
return pool
}
go
func (p *WorkerPool) worker() {
for job := range p.Jobs {//这里就是从公共通道里面获取任务
job()
}
}
以上,最简单的协程池就算创建好了。
协程池的使用
go
func (p *WorkerPool) AddJob(job func()) {
p.Jobs <- job
}
这里就是将任务函数传进通道里,然后公平地分给各个空闲的协程。
协程池的关闭
go
func (p *WorkerPool) Close() {
close(p.Jobs)
}
完整健壮的协程池
go
type WorkerPool struct {
Jobs chan func()
MaxWorkers int
wg sync.WaitGroup // 新增:任务完成计数器
}
func NewWorkerPool(maxWorkers int) *WorkerPool {
pool := &WorkerPool{
Jobs: make(chan func()),//初始化
MaxWorkers: maxWorkers,
}
for i := 0; i < pool.MaxWorkers; i++ {
go pool.worker()//这里就是创建多个协程
}
return pool
}
func (p *WorkerPool) worker() {
for job := range p.Jobs {//这里就是从公共通道里面获取任务
defer func() {
if r := recover(); r != nil {
log.Printf("Worker panic: %v", r)
}
}()
job()
}
}
func (p *WorkerPool) AddJob(job func()) {
p.wg.Add(1) // 任务数+1
p.Jobs <- func() { //这里的func匿名函数就是上面的job处理函数
defer p.wg.Done()
job() // 执行实际任务
}
}
func (p *WorkerPool) Wait() {
p.wg.Wait() // 阻塞直到所有任务完成
}
func main() {
pool := NewWorkerPool(2)
// 添加 3 个任务
pool.AddJob(func() { time.Sleep(1 * time.Second) })
pool.AddJob(func() { time.Sleep(1 * time.Second) })
pool.AddJob(func() { time.Sleep(1 * time.Second) })
// 主协程等待任务完成
defer fmt.Println("主协程结束")
pool.Wait() // **关键:阻塞在这里直到所有任务完成**
}
上面的代码添加了
go
sync.WaitGroup
defer func() {
if r := recover(); r != nil {
log.Printf("Worker panic: %v", r)
}
}()
第一个用来确保在channel中的时间完全完成之后才可以结束文件。
第二个用来确保在每个任务出现问题时不会影响整个协程池的正常运行