つくる: 並行集計
カテゴリごとに同時に数える
第7章で学んだ起動と待ち合わせを組み合わせます。小計計算と表示は用意済みなので、今回は並行処理の組み立てに集中します。
ゴルーチン go で走らせる
チャネル 結果を受け取る
WaitGroup 完了を待つ
Mutex 共有する変数を守る作るのは、カテゴリ別の売上集計です。カテゴリごとに1つのゴルーチンを立てて、同時に計算します。
本:2000 家電:248003つの書き方から選ぶ
並行に集計して結果をまとめる方法は、いくつかあります。
1. チャネルで受け取る 入れた数だけ取り出す
2. スライスの別の場所に書く 衝突しないので Mutex 不要
3. マップを Mutex で守る 共有するので保護が要る2番がいちばん素直です。 カテゴリの一覧を先に作っておけば、それぞれの結果を別の添字に書けます。同じ要素を共有せず、読み取りは全件の完了後に行うので、Mutex も要りません。
第7章で「まず分けるか渡すかを考える」と書いたのは、こういう場面のことです。
順番を保つ
マップを使うと、出力の順番が毎回変わります。第5章でやったとおりです。
この演習ではカテゴリの登場順に並べてもらいます。スライスで順番を持てば、並行に計算しても順番は崩れません。ここも2番の書き方が効く理由です。
手順
1. カテゴリの一覧を、登場順に重複なく集める
2. 一覧の長さぶんの結果スライスを用意する
3. カテゴリごとにゴルーチンを立て、合計を該当の添字へ書く
4. WaitGroup で全部待つ
5. 一覧の順に文字列を組み立てる1 は並行処理ではありません。先に順番を確定させてから並行に走らせる、という段取りです。ここを並行にすると順番が崩れます。
引数で渡すのを忘れない
Go
// 各地点の気温を別々の要素へ記録する例
go func(idx int, place string) {
defer wg.Done()
readings[idx] = temperatureAt(place)
}(i, location)引数を渡して go func(...) { ... }(...) と呼ぶと、無名関数をゴルーチンとして起動できます。添字と処理対象を引数にすると、どの値を渡すかが明確になります。Go 1.22 以降の range 変数は反復ごとに作られるので、引数にしなければ必ず壊れるという意味ではありません。
演習の sumCategory は、カテゴリと価格を同じ添字で読み、指定カテゴリだけを足して返します。入力と表示の処理は書き直さず、起動前の Add、終了時の Done、結果の格納を担当してください。
要件
- 用意済みのカテゴリ一覧
orderごとにゴルーチンを起動する - 起動の前に Add、ゴルーチン内で defer Done を使う
sumCategoryの結果をresults[idx]に入れる- 用意済みの Wait と整形を使い、登場順を保つ
ヒント
編集 ゆめさく編集部