✨
GoのWaitGroupを理解する
WaitGroup
goルーチンを使っていると、すべてのルーチンが完了するまで待機したい場合があるかと思います。
そういう時はsyncパッケージのWaitGroupを使うと便利です。
使い方
Addメソッド,Doneメソッド,Waitメソッドがあり、waitgroupに追加するためにまずAddを行います。Addの引数で渡した数だけ、Done()を呼び出すまで、Wait()が処理をブロックしてくれます。
var wg sync.WaitGroup
wg.Add(1)
go func() {
/*何らかの処理*/
wg.Done()
}()
wg.Add(1)
go func() {
/*何らかの処理*/
wg.Done()
}()
wg.Wait()
fmt.Println("タスクがすべて完了")
試してみる
実際に挙動を見てみましょう。まずはWaitGroupを使わなかった場合です。
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("開始")
go func() {
/*5秒待機*/
time.Sleep(5 * time.Second)
fmt.Println("end timer1")
}()
go func() {
/*5秒待機*/
time.Sleep(5 * time.Second)
fmt.Println("end timer2")
}()
fmt.Println("終了")
}
//実行結果
開始
終了
上記の例は、time.Sleepを使用して5秒間待機するgoルーチンを2つ実行していますが、当然終了を待っていないので、fmt.Println("終了")が呼ばれてプログラムが終了します。
では、WaitGroupを使ってみます。
package main
import (
"fmt"
"sync"
"time"
)
func main() {
fmt.Println("開始")
var wg sync.WaitGroup
wg.Add(1)
go func() {
/*5秒待機*/
time.Sleep(5 * time.Second)
fmt.Println("end timer1")
wg.Done()
}()
wg.Add(1)
go func() {
/*5秒待機*/
time.Sleep(5 * time.Second)
fmt.Println("end timer2")
wg.Done()
}()
wg.Wait()
fmt.Println("終了")
}
//実行結果
開始
end timer1
end timer2
終了
プログラムを実行すると、開始が表示されて5秒間待機されてから終了が出力されてプログラムが終了されるようになりました!!
以下のようにfor文で回しても使えます。
var wg.WaitGroup
for i := 0; i < 10 ; i++ {
wg.Add(1)
go func() {
/* do something */
wg.Done()
}()
}
wg.Wait()
使いどころ
今回はtime.Sleepで挙動を確認しましたが、goルーチンを使って、複数のAPIをたたいたり、複数ページのスクレイピングをしたり、それぞれのgoルーチンの終了タイミングが異なるときなどに便利です。
参考
最後に
WaitGroupは便利なので使っていきましょう。
次は、traceパッケージやsync.Mutexについて書こうかなと思います。
Discussion