問8 3本同時に走らせろ
中段 / 目安 35分
「リプレイスの動機、見せましょう」
凛さんがグラフを出しました。「3カテゴリの集計、Node版は順番に処理して3秒。Go なら同時に走らせて1秒です。ゴルーチンで証明してください」
ゴルーチンは、関数を別の流れで走らせる仕組みです。go を付けるだけです。
Go
go collect("shoes")これだけで発射できますが、2つ困ったことが起きます。
1つ目は、結果の受け取り方です。 発射した関数の戻り値は、そのままでは受け取れません。そこでチャネルを使います。
Go
ch := make(chan int, 3)
ch <- 42 // 入れる
v := <-ch // 取り出す矢印は値が動く向きです。ch <- が入れる、<-ch が取り出すです。
2つ目は、終わるのを待つことです。 main が先に終わると、走っている途中のゴルーチンは道連れで消えます。WaitGroup で全員を待ちます。
Go
var wg sync.WaitGroup
wg.Add(1) // 1人増えた
go func() {
defer wg.Done() // 終わったら報告
}()
wg.Wait() // 全員終わるまで待つdefer は「この関数を抜けるときに必ず実行する」という指示です。途中で return してもエラーが起きても呼ばれるので、Done の呼び忘れを防げます。
もう1つ大切なことがあります。ループの中でゴルーチンを作るとき、ループ変数をそのまま使わないでください。全員が同じ変数を見てしまい、期待どおりに動かないことがあります。引数として渡すのが確実です。
完成条件
3つのカテゴリの売上を、ゴルーチンで同時に集計する関数 collectAll を作ってください。
3つの数を受け取り、それぞれを別のゴルーチンで2倍にしてチャネルへ送り、全部の合計を返します。
WaitGroup で3つとも終わるのを待ってから、チャネルを閉じて合計してください。待たずに数えると、間に合わなかった分を取りこぼします。
要件
- 3つの数をそれぞれ別のゴルーチンで処理する(
goを使う) - 結果はチャネルで回収し、
WaitGroupで全部の完了を待つ - ループ変数をゴルーチンへ直接渡さず、引数として渡す
入出力例
collectAll(1200, 3400, 800) → 10800
collectAll(100, 100, 100) → 600
collectAll(0, 2500, 0) → 5000ヒント
前のヒントを開くと次が開きます。開かずに解けると未開封クリアです
ヒント 2ヒント 1 を開くと読めます
ヒント 3ヒント 2 を開くと読めます
編集 ゆめさく編集部