概要
Golangの並行処理をしつつerrorハンドリングをしたい場合は、errorgroupパッケージを使うことができます。
GoDocはこちら
https://godoc.org/golang.org/x/sync/errgroup
とりあえず実装してみる(期待していない値が返ってくる例)
import "golang.org/x/sync/errgroup"
var g errgroup.Group
for _, url := range urls {
g.Go(func() error {
// Fetch the URL.
resp, err := http.Get(url)
if err == nil {
resp.Body.Close()
}
return err
})
}
こんな感じで書くと、urlsの末尾の要素のみ参照してしまい、期待通りに動作しません。なぜなのか。
GoDocを読むと以下のように(後章に記載しています)サンプルコードが書いてあります。
期待通りに動作する例
import "golang.org/x/sync/errgroup"
var g errgroup.Group
for _, url := range urls {
// Launch a goroutine to fetch the URL.
url := url // https://golang.org/doc/faq#closures_and_goroutines <- この代入の意味はなんだろう :thinking_face:
g.Go(func() error {
// Fetch the URL.
resp, err := http.Get(url)
if err == nil {
resp.Body.Close()
}
return err
})
}
コメントに記載したリンク( https://golang.org/doc/faq#closures_and_goroutines )に飛ぶと以下の文とサンプルコード発見
Even easier is just to create a new variable, using a declaration style that may seem odd but works fine in Go:
このように記述するとより簡単に書くことができます。
This behavior of the language, not defining a new variable for each iteration, may have been a mistake in retrospect. It may be addressed in a later version but, for compatibility, cannot change in Go version 1.
新しい値をイテレーションの中で定義するのは、振り返ると言語仕様として間違っていたが、Goの1系では直せない。
まとめ
errorgroupではこのイテレーションの方法を取るしかなさそう。
ということで、イテレーションの中でerrorgroupを利用する場合はご注意下さい。
関連記事
この記事を執筆した後に、過去に同様の内容の記事が投稿されていましたのでリンクしておきます。
https://qiita.com/sudix/items/67d4cad08fe88dcb9a6d