在Golang中,可以使用sync.WaitGroup
来实现并发调度器。WaitGroup
提供了一种简单的方式来等待一组并发任务完成。
但是,在大规模并发场景下,WaitGroup
可能存在性能问题。每次调用Wait()
方法都会阻塞当前的goroutine,直到所有并发任务完成。这可能会导致过多的goroutine被创建,从而降低性能。
为了优化WaitGroup
的性能,可以使用有缓冲的通道来实现。具体步骤如下:
- 创建一个结构体
Pool
,包含一个有缓冲的通道和一个sync.WaitGroup
。
type Pool struct { wg sync.WaitGroup worker chan struct{} }
- 初始化
Pool
,并设置通道的容量为并发的最大任务数。
func NewPool(maxWorkers int) *Pool {
return &Pool{
worker: make(chan struct{}, maxWorkers),
}
}
- 在每个并发任务开始前,调用
Add()
方法增加WaitGroup
的计数器,并向通道发送一个空结构体,表示可用的goroutine。
func (p *Pool) Add() { p.wg.Add(1) p.worker <- struct{}{} }
- 在每个并发任务结束后,调用
Done()
方法减少WaitGroup
的计数器,并从通道中接收一个空结构体,表示该goroutine已完成任务。
func (p *Pool) Done() { p.wg.Done() <-p.worker }
- 在需要等待所有并发任务完成的地方,调用
Wait()
方法等待WaitGroup
的计数器归零。
func (p *Pool) Wait() { p.wg.Wait() }
使用优化后的Pool
结构体来替代sync.WaitGroup
,可以避免过多的goroutine被创建,从而提高并发调度的性能。以下是一个完整的示例代码:
package main
import (
"fmt"
"sync"
"time"
)
type Pool struct {
wg sync.WaitGroup
worker chan struct{}
}
func NewPool(maxWorkers int) *Pool {
return &Pool{
worker: make(chan struct{}, maxWorkers),
}
}
func (p *Pool) Add() {
p.wg.Add(1)
p.worker <- struct{}{}
}
func (p *Pool) Done() {
p.wg.Done()
<-p.worker
}
func (p *Pool) Wait() {
p.wg.Wait()
}
func main() {
pool := NewPool(3)
for i := 0; i < 10; i++ {
pool.Add()
go func(i int) {
defer pool.Done()
time.Sleep(time.Second)
fmt.Printf("Task %d done\n", i)
}(i)
}
pool.Wait()
fmt.Println("All tasks done")
}
运行以上代码,会创建一个最大并发数为3的并发调度器,模拟10个任务的执行。每个任务会休眠1秒钟后输出完成信息。在等待所有任务完成后,输出"All tasks done"。
通过使用优化后的Pool
结构体,可以有效地控制并发调度的性能,避免过多的goroutine被创建。