Go言語学習の5日目です。
今日も前回の続きです。Webサービスのレスポンスタイムを計測するCLIツールに、並行処理を実装しました。
並行処理を実装するための方法として、1).sync.WaitGroupを使う方法と、2).バッファなしチャネルを使う方法があります。
sync.WatiGroupを使って実装
// tryGetUrlはurlに対してGetメソッドを叩いて結果を出力するメソッドです
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go func(){
defer wg.Done()
tryGetUrl(url)
}()
}
wg.Wait()
まずはまったポイントとして、Goの無名関数の書き方があります。 func() {...}()
とあり、1つ目の()は引数、{}は処理の中身だとすぐに理解できました。ただ、最後の()の意味が分かりませんでした。これは無名関数を即座に実行するためのものです。
// 実行コマンド
go run . https://example.com https://google.com https://yahoo.co.jp
// 実行結果
[✓]{https://yahoo.co.jp} -200 OK -132ms
[✓]{https://example.com} -200 OK -354ms
[✓]{https://google.com} -200 OK -386ms
前回のforで回していた時は、引数の順番通りにexample.comからGetアクセスして結果を出力していました。並列処理を取り入れた今回は、必ずしもその順番で出力されるわけではありません。
チャネルを使った実装
今度はチャネルを使って、同じような処理を実装してみました。最初私は下記のように書きました(間違っています)
results := []Result{}
for _, url := range urls {
c := make(chan Result)
go tryGetUrl(c,url)
result := <-c
results = append(results, result)
}
fmt.Println(results)
fmt.Println("プログラム終了------")
各URLに対してゴールーチンを起動していますが、チャネルを使って結果を受け取る部分が各ゴールーチンの直後にあるため、実質的に逐次処理になってしまいます。
// 実行コマンド
go run . https://example.com https://google.com https://yahoo.co.jp
// 出力結果
[{https://example.com 200 OK 377} {https://google.com 200 OK 383} {https://yahoo.co.jp 200 OK 144}]
正しく並行処理を実装するために、すべてのゴールーチンを起動した後に結果を集めるようにします。
results := []Result{}
c := make(chan Result)
for _, url := range urls {
go tryGetUrl(c, url)
}
for range urls {
result := <-c
results = append(results, result)
}
fmt.Println(results)
fmt.Println("プログラム終了------")
また、チャネルをバッファなしにするか、バッファありにするかという問題があります。まだこれらにどのような違いがあるのか分かっていません。今後、勉強していきます。
次回はEbitengineを使って2Dゲームを作ってみようと思います。