goroutineの同時実行数を制御するやつ
goroutineは気軽に使えるのでバコバコ使ってしまうけど、同時に実行されすぎると困ることがある。
例えばサイトのスクレイピングとかで同時に3アクセスまでに制限したいとか。
だいたいこんな感じのコードになる。
package main import ( "sync" ) func main() { ch := make(chan struct{}, 3) wg := &sync.WaitGroup{} arr := []string{"https://a.com/", "https://b.com/", "https://c.com/", "https://d.com/"} for i := 0; i < len(arr); i++ { wg.Add(1) go func(j int) { ch <- struct{}{} defer wg.Done() // urlにアクセスしてなんかする doScraping(arr[j]) <-ch }(i) } wg.Wait() }
同時に実行されるgoroutineの数を制限するためには同時に処理されたい最大値をcapacityに持つchannelを作るという手法を使う。
慣れるとかんたんなことなんだけど、少しわかりづらいなーと思うことがあったのでこんな感じで制御したいという願望を込めた小さいライブラリを作った。
上記のコードのchannelの生成とかstructの追加のところを構造体のメソッド化してるだけ。
police.Limit()で同時実行数を指定して、police.Block()からpolice.Release()で囲む。
package main import ( "sync" "github.com/YuheiNakasaka/police" ) func main() { // initialize police := &police.Arrival{} // set the count of goroutine processed at the same time police.Limit(3) wg := &sync.WaitGroup{} arr := []string{"https://a.com/", "https://b.com/", "https://c.com/", "https://d.com/"} for i := 0; i < len(arr); i++ { wg.Add(1) go func(j int) { police.Block() defer wg.Done() // urlにアクセスしてなんかする doScraping(arr[j]) police.Release() }(i) } wg.Wait() }
ぶっちゃけコード量はchannelを直で使うより増えてるんだけど、なんとなくコードは読みやすくなるかなという感じがする。
ちなみに警察官が交通整理してるみたいなイメージでpoliceという名前になってる。
大した処理じゃないけどこういうのもありかなという感じで作った。
ほんとはsync.WaitGroupにLimitみたいなメソッドが生えてればいいのかもと思ったけど、そういうもんでもないんですかね、Goの文化的に。
小ネタでした。