Go言語のチャネルについて、バッファ(Buffered Channnels)のところで少し触れたのですが、チャネルのrangeとclose()についてもう少し見ていきます。
ここではゴルーチンを絡めたチャネルのrangeとclose()を扱います。
チャネルのclose()
rangeとclose()については、バッファのところで少し扱いました。
そこでは、単に関数の中でチャネルに値を複数渡してrangeで取り出す時のclose()を設定したのですが、ここでは、ゴルーチンを使った並行処理としてデータの受け渡しをしているコードを見ていくことにします。
次のようなコードで、まずチャネルのclose()を確認しておきましょう。
package main
import "fmt"
// チャネルに値を渡す
func sendValue(c chan<- int) {
c <- 100
}
// チャネルから値を受け取る
func receiveValue(c <-chan int) {
fmt.Println("チャネルから受け取った値:", <-c)
}
func main() {
c := make(chan int)
go sendValue(c)
receiveValue(c)
close(c)
fmt.Println("処理を終了します。")
}
sendValue()でチャネルに値を渡す処理を、receiveValue()でチャネルから値を受け取る処理を書いています。
main()で、チャネルを設定し、ゴルーチンでsendValue()を呼び出し、receiveValue()を呼び出して、close()でチャネルを閉じています。
処理の終了をPrintln()で最後に出力しています。
実行すると次のように出力されます。
チャネルから受け取った値: 100
処理を終了します。
特に問題は無い処理だと思います。
ここではチャネルのバッファを設定せず、しかも1つのチャネルしか使っていないので、close()を記述しなくてもエラーにはならず、同じ結果が得られます。
rangeとclose()
上記のコードを修正してrangeを使った処理にしてみましょう。
package main
import "fmt"
// チャネルに値を渡す
func sendValue(c chan<- int) {
for i := 0; i < 5; i++ {
c <- i
}
close(c)
}
// チャネルから値を受け取る
func receiveValue(c <-chan int) {
for v := range c {
fmt.Println("チャネルから受け取った値:", v)
}
}
func main() {
c := make(chan int)
go sendValue(c)
receiveValue(c)
fmt.Println("処理を終了します。")
}
sendValue()でチャネルに値を渡す処理を、for文を使って反復処理でチャネルに値を渡しています。ここでチャネルをclose()で終了させています。
receiveValue()でチャネルから値を受け取っていますが、この処理にrangeを使って値を順次取り出しています。
main()で、チャネルを設定し、ゴルーチンでsendValue()を呼び出し、receiveValue()を呼び出して、処理の終了をPrintln()で最後に出力しています。
実行結果は次のようになります。
チャネルから受け取った値: 0
チャネルから受け取った値: 1
チャネルから受け取った値: 2
チャネルから受け取った値: 3
チャネルから受け取った値: 4
処理を終了します。
このようにゴルーチンを使った並行処理の中でも、rangeを使ってチャネルから値を取り出す処理は、close()をつけて処理をします。
このclose()をつけていないと次のようにエラーになってしまいます。
チャネルから受け取った値: 0
チャネルから受け取った値: 1
チャネルから受け取った値: 2
チャネルから受け取った値: 3
チャネルから受け取った値: 4
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.receiveValue(0xc000116000)
/Users/(goの実行ファイルへのパス)
main.main()
/Users/(goの実行ファイルへのパス)
exit status 2
値が順に取り出させていますが、チャネルの終了が無いためにさらに値を取り出そうとしてエラーになっています。
最後に
Go言語のゴルーチンを使った並行処理におけるチャネルのrangeとclose()について扱いました。
rangeを使ってチャンネルの値を取り出す時は、チャネルの終了をclose()を使って示しておく必要があります。これが無い場合は値をさらに取り出し続けようと処理を繰り返すことになり、エラーが表示されることになります。