Go言語のチャネル(Channel)の基本を見てきましたが、チャネルには受入れらるキャパシティを設定することができます。
これをバッファといいます。
ここではチャネルのバッファ(Buffered Channels)を見ていきましょう。
バッファ
チャネルのバッファは次のような形で記述します。
c := make(chan 型名, バッファ数)
特に難しいところは無いと思います。チャネルの設定時にバッファの数を指定するだけです。
具体的なコードで見てみましょう。
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 2020
c <- 2021
fmt.Println(<-c)
}
ここはバッファを2としました。
チャネルに渡した値をPrintln()で出力しています。
出力結果は次のようになります。
2020
2021
チャネルに渡した2つの値を取り出せて出力できています。
このコードを次のように修正してみます。
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 2020
c <- 2021
c <- 2022
fmt.Println(<-c)
fmt.Println(<-c)
fmt.Println(<-c)
}
チャンネルのバッファは2のままです。
そこで、チャンネルに2つの値を渡して出力しています。
実行すると次のようにエラーになります。
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/Users/(goの実行ファイルへパス)
exit status 2
チャンネルのキャパシティが2つのところに3つ目の値を入れようとしたために、エラーになったわけです。
では、このコードを次のようにするとどうなるでしょうか?
package main
import "fmt"
func main() {
c := make(chan int, 2)
c <- 2020
c <- 2021
fmt.Println(<-c)
c <- 2022
fmt.Println(<-c)
fmt.Println(<-c)
}
チャネルの2つの値を渡したあと、1つ取り出して出力した後に、値を再度追加しています。
実行するとこうなります。
2020
2021
2022
今度はエラーにならずに出力ができました。
これは、2つバッファが埋まった後に、一回値を取り出したことで、チャンネルのバッファの数が1に減ってキャパシティーが空いたので、そこに値を追加することができるようになったからです。
close()
ここまでは、チャンネルに渡した値を一つずつPrintln()で取り出してきました。
次のコードの処理のことですね。
package main
import "fmt"
func main() {
c := make(chan int, 3)
c <- 2020
c <- 2021
c <- 2022
fmt.Println(<-c)
fmt.Println(<-c)
fmt.Println(<-c)
}
順に、2020、2021、2022と出力されるのは実行しなくてもわかるでしょう。
このコードを、for文を使って値を取り出す形にしようと思います。
次のようなコードで取り出してみます。
package main
import "fmt"
func main() {
c := make(chan int, 3)
c <- 2020
c <- 2021
c <- 2022
for v := range c {
fmt.Println(v)
}
}
for文とrangeを使ってチャネルから値を取り出して出力しています。
実行するとこうなります。
2020
2021
2022
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/Users/(goの実行ファイルへのパス)
exit status 2
値が3つ出力されているのですが、続けてエラーが表示されています。
これは、fo文のrangeで値を順にチャネルから取り出しているのですが、取り出し終わってもチャネルに値を取りに行こうと処理を続けようとしているからです。
こうならないようにするには、次のようにコードにclose()を追加します。
package main
import "fmt"
func main() {
c := make(chan int, 3)
c <- 2020
c <- 2021
c <- 2022
close(c)
for v := range c {
fmt.Println(v)
}
}
チャネルに値を渡した後に、close(c)でチャネルを閉じています。
実行するとこうなります。
2020
2021
2022
今度はエラーも出ずに出力できました。
このように、rangeで取り出す時は、close()でチャネルが終了していることを記述してやる必要があります。
最後に
ここでは、Go言語のチャネルの操作でのバッファを扱いました。
バッファを設定することで、チャネルの容量を設定することができます。
バッファで設定した容量を超えて、チャネルに値を渡すことはできませんが、値を途中で取り出すことで値を再度渡すことはできます。
rangeを使ってチャンネルの値をfor文で取り出す時は、チャネルの終了をclose()を使って示しておく必要があります。