在 Golang 中,协程池(goroutine pool)是管理一组可复用 goroutine 的机制。它可以实现限制 goroutine 数量,复用 goroutine 等效果。
goroutine 池常见的实现方式有:
- 无大小限制的池:
func worker(jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int)
for i := 0; i < 10; i++ { // 开启10个goroutine
go worker(jobs, results)
}
for j := 0; j < 100; j++ {
jobs <- j
}
close(jobs)
for a := 0; a < 100; a++ {
<-results
}
}
这里简单地开启 10 个 goroutine,没有限制池大小。
- 固定大小的池:
func worker(jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int)
var wg sync.WaitGroup
for i := 0; i < 10; i++ { // 限制池大小为10
wg.Add(1)
go func() {
defer wg.Done()
worker(jobs, results)
}()
}
for j := 0; j < 100; j++ {
jobs <- j
}
close(jobs)
wg.Wait() // 等待所有goroutine结束
close(results)
}
这里限制池大小为 10,通过 wg.Add(1) 和 wg.Done() 实现计数。
3.带缓冲的池:
func worker(jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
var wg sync.WaitGroup
for i := 0; i < 10; i++ { // 限制池大小为10
wg.Add(1)
go func() {
defer wg.Done()
worker(jobs, results)
}()
}
for j := 0; j < 100; j++ {
jobs <- j
}
close(jobs)
wg.Wait() // 等待所有goroutine结束
close(results)
}
这里给 results 添加了缓冲,可以存放一定数量的结果,避免 goroutine 积压。
所以 goroutine 池的主要作用是:
- 限制 goroutine 的数量,避免创建过多 goroutine 导致内存爆炸。
- 复用 goroutine,减少创建和销毁 goroutine 的开销。
- 在一定程度上实现流量控制,避免 goroutine 洪水。
goroutine 池常用于实现工作线程池、任务队列调度等场景。熟练掌握 goroutine 池的实现方式,可以帮助我们设计出更加高效和稳定的并发程序。
总之,goroutine 池是管理 goroutine 的一种重要机制。合理使用可以充分发挥 goroutine 的优势,同时减少由于大量 goroutine 导致的性能损耗与资源消耗。