golang,go,博客,开源,编程
在 Go 中,协程池(goroutine pool)是为了避免在高并发的场景下频繁创建和销毁协程,从而消耗大量资源。协程池通过限制并发协程的数量来更好地控制系统资源的使用,通常用于处理高并发任务,尤其是当任务的数量可能会非常大,或者任务的处理非常轻量时。
协程池的基本思想是提前创建一定数量的协程,在池中预分配并管理这些协程,当任务到来时从池中获取一个可用的协程来执行任务,任务完成后将协程归还给池中等待下一个任务。
Go 并没有内建的协程池,因此我们通常需要自己实现协程池。这里介绍一个简单的协程池实现:
sync.WaitGroup
来等待所有任务完成。package main
import (
"fmt"
"sync"
"time"
)
type GoroutinePool struct {
// 任务队列
taskQueue chan func()
// 等待所有任务完成
wg sync.WaitGroup
}
func NewGoroutinePool(poolSize int) *GoroutinePool {
return &GoroutinePool{
taskQueue: make(chan func(), poolSize), // 设置队列大小为协程池大小
}
}
// 启动池中的协程
func (p *GoroutinePool) Start() {
for i := 0; i < cap(p.taskQueue); i++ {
go p.worker(i)
}
}
// 工作协程
func (p *GoroutinePool) worker(id int) {
for task := range p.taskQueue {
task() // 执行任务
p.wg.Done()
}
}
// 提交任务到协程池
func (p *GoroutinePool) Submit(task func()) {
p.wg.Add(1) // 增加等待任务完成的计数
p.taskQueue <- task
}
// 等待所有任务完成
func (p *GoroutinePool) Wait() {
p.wg.Wait()
}
func main() {
poolSize := 3
pool := NewGoroutinePool(poolSize)
pool.Start()
// 提交任务
for i := 0; i < 10; i++ {
taskID := i
pool.Submit(func() {
fmt.Printf("Task %d is running\n", taskID)
time.Sleep(1 * time.Second) // 模拟任务执行时间
fmt.Printf("Task %d is done\n", taskID)
})
}
// 等待所有任务完成
pool.Wait()
fmt.Println("All tasks completed.")
}
GoroutinePool
结构体:
taskQueue
:任务队列,大小为池的容量(即最大并发协程数)。任务是通过 chan func()
提交到池中的。wg
:用来等待所有任务完成的 sync.WaitGroup
。NewGoroutinePool
:
Start
方法:
worker
方法:
wg.Done()
表示任务完成。Submit
方法:
WaitGroup
的计数。Wait
方法:
wg.Wait()
。在上述示例中,我们创建了一个最大并发数为 3 的协程池,并提交了 10 个任务。由于池中最多只能有 3 个协程并发执行任务,因此最多会有 3 个任务同时运行。任务的执行是并发的,执行过程中每个任务会输出开始和结束的日志。
上面的实现已经是一个简单的协程池实现,但在实际使用中,还可以进行一些优化和改进:
context
)来取消正在执行的任务。协程池是 Go 语言中非常实用的一种并发控制方法,它通过限制并发的协程数量,避免了系统资源的过度消耗,尤其在需要频繁执行轻量级任务时,协程池能够大大提高性能并减少资源浪费。通过合理设计协程池的大小、任务提交和管理机制,可以提高系统的稳定性和并发处理能力。