ゴルーチン
go と書くだけ
Go
go doSomething()関数呼び出しの前に go を付けます。これだけで、その処理は別の流れで走り始めます。呼んだ側は結果を待たずに次の行へ進みます。
この別の流れを ゴルーチン と呼びます。Go という名前がそのまま入っているとおり、この言語の看板機能です。
何がうれしいのか
待ち時間のある処理が並ぶと、順番に実行するのは無駄です。
プレーンテキスト
順番に 商品を数える(1秒) → 売上を数える(1秒) → 合計2秒
同時に 両方を同時に始める → 合計1秒サーバーは、この「待っている時間」の塊です。データベースの応答待ち、外部APIの応答待ち。その間に別のリクエストを処理できれば、同じ機械でずっと多くを捌けます。Go がサーバー向きと言われるのは、これが1語で書けるからです。
いきなり困ること
Go
func main() {
go fmt.Println("ゴルーチンから")
fmt.Println("main から")
}プレーンテキスト
main からゴルーチンの出力が出ません。
main が終わると、プログラム全体が終わります。走り始めたゴルーチンは、途中でも打ち切られます。誰も待ってくれません。
順番が決まらない
Go
for i := 1; i <= 3; i++ {
go fmt.Println(i)
}
time.Sleep(100 * time.Millisecond) // 終わるまで少し待つ出力の順番は毎回変わります。1 2 3 のこともあれば、3 1 2 のこともあります。
最後の1行が無いと、何も出力されません。さきほどの話と同じで、main が
終わった時点で3つとも打ち切られるからです。ここでは順番を見せたいので
time.Sleep で待っていますが、これは説明のための仮のやり方です。
本当の待ち合わせは次のレッスン以降で扱います。
どちらが先に終わるかは決まっていません。 これは不具合ではなく、並行処理の性質そのものです。順番が要るなら、それを保証する仕組みを別に用意します。
だから、この章の残りがある
ゴルーチンは始めるのが1語で済む代わりに、終わりを扱う道具が要ります。
プレーンテキスト
チャネル 結果を受け取る 次のレッスン
WaitGroup 全部の完了を待つ その次
Mutex 同じ変数を守る その次の次go と書くのは簡単で、正しく待つのが難しい。第7章はその難しさのほうを扱います。
このコースの演習では
採点は関数の戻り値で行うので、結果を必ず回収してから返す形になります。次のレッスン以降で、その書き方を身につけます。今回は「待たないと結果が消える」ことを、コードで確かめてもらいます。
要件
- 各要素を2倍にする処理をゴルーチンで走らせる
- 結果をチャネルで受け取る
- 要素数と同じ回数だけ取り出して合計する
- 要素が0件のときは 0 を返す
入出力例
runTasks([1,2,3]) → 12
runTasks([5]) → 10
runTasks([0,4]) → 8
runTasks([2,2,2]) → 12