Goの並行スレッドの実装:Go WaitGroupの最適化実装
Go言語では、並行ジョブスケジューラを作成するときにsync.WaitGroupを使用できます。WaitGroupは、同時に複数のタスクの完了を待つ簡単な方法を提供します。
大規模並列処理シナリオでは、WaitGroupはパフォーマンスの問題を引き起こす可能性があります。 Wait() メソッドを呼び出すたびに、すべての並列タスクが完了するまで現在のゴルーチンをブロックします。これにより過剰なゴルーチンが作成され、パフォーマンスが低下する可能性があります。
WaitGroup パフォーマンスを向上させるのには、バッファ付きチャネルを導入する方法がある。手順は下記の通り。
- プール
- 同期用WaitGroup
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
}
- 待つ()
- 同期 WaitGroup
func (p *Pool) Wait() {
p.wg.Wait()
}
最適化された Pool 構造体を使用することで、過剰な 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の過剰な生成を防止できます。