チャネル
ゴルーチンの間で値を渡す
Go
ch := make(chan int) // int を流すチャネル
ch <- 42 // 入れる
v := <-ch // 取り出す矢印が向きを表します。ch <- はチャネルへ入れる、<-ch はチャネルから取り出す、です。矢印はいつも値の流れる向きを指しています。
待ち合わせの道具でもある
チャネルの本当の役目は、値を運ぶことだけではありません。タイミングを合わせることです。
Go
v := <-chこの行は、値が入るまでそこで止まります。入った瞬間に動き出します。
前のレッスンで「待たないと結果が消える」問題がありました。チャネルから取り出す行を書けば、そこで待つので消えません。
容量のあるチャネル
Go
ch := make(chan int) // 容量0
ch := make(chan int, 3) // 容量3容量0のチャネルは、入れる側も取り出す側が来るまで待ちます。手渡しに近い動きです。
Go
ch := make(chan int)
ch <- 42 // 取り出す人がいないと、ここで永久に止まる1人で書いていると、これで固まります。
プレーンテキスト
fatal error: all goroutines are asleep - deadlock!デッドロックです。全員が誰かを待っていて、誰も進めない状態を Go が検出して教えてくれます。
容量があれば、その数までは待たずに入れられます。
Go
ch := make(chan int, 3)
ch <- 1 // 待たない
ch <- 2 // 待たない前のレッスンで make(chan int, len(nums)) としたのは、これが理由です。全部入れきってから取り出せるので、順番を気にせずに済みます。
取り出す回数を合わせる
最も間違えやすいのがここです。
プレーンテキスト
入れた数 > 取り出す数 結果が足りない
入れた数 < 取り出す数 永久に待つ。タイムアウト入れた数をきっちり数えて、同じ回数だけ取り出します。この「数を合わせる」作業が面倒なので、次のレッスンの WaitGroup が用意されています。
range で受け取る
Go
for v := range ch {
total += v
}チャネルが閉じられるまで受け取り続けます。閉じるのは送る側です。
Go
close(ch)閉じ忘れると、range は永久に待ちます。入門のうちは回数を数えて取り出す形で十分なので、close は「そういうものがある」と知っておく程度で構いません。
要件
- 前半と後半をそれぞれゴルーチンで集計する
- 結果をチャネルで受け取る
- 2つの結果を足して返す
- 0件のときは 0 を返す
入出力例
parallelSum([100,200,300,400]) → 1000
parallelSum([100,200,300]) → 600
parallelSum([500]) → 500
parallelSum([0,0,100]) → 100ヒント
編集 ゆめさく編集部