チャネルのブロッキング
注意深い学生たちは、チャネルの宣言と初期化を紹介した際に、チャネルの容量を指定していないことに気付いたかもしれません。
package main
import "fmt"
func main() {
// チャネルに整数データを格納する
ch1 := make(chan int) // 以下も同じ
// チャネルにブールデータを格納する
ch2 := make(chan bool)
// チャネルに []int データを格納する
ch3 := make(chan []int)
fmt.Println(ch1, ch2, ch3)
}
しかし、チャネルの操作を示す際には、チャネルの容量を指定していました。
package main
import "fmt"
func main() {
// チャネル容量を3に指定する
ch := make(chan int, 3)
ch <- 10
ch <- 20
fmt.Println(<-ch)
fmt.Println(<-ch)
}
容量が指定されていないチャネルは、バッファリングされていないチャネルと呼ばれ、バッファ空間がありません。
送信者または受信者が準備ができていない場合、最初の操作は、もう一方の操作が準備できるまでブロックされます。
chan1 := make(chan int) // int型のバッファリングされていないチャネル
容量とバッファ空間が指定されているチャネルは、バッファリングされたチャネルと呼ばれます。
chan2 := make(chan int, 5) // 容量5のint型のバッファリングされたチャネル
これらはキューのように機能し、最初に入ったものが最初に出る(FIFO)ルールに従います。
バッファリングされたチャネルの場合、送信操作を使用してキューの末尾に要素を追加し、受信操作を使用してキューの先頭から要素を削除できます。
バッファリングされたチャネルに容量を超えるデータを入れるとどうなるでしょうか。確認してみましょう。channel1.go
ファイルに以下のコードを書き込みます。
cd ~/project
touch channel1.go
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10
ch <- 20
fmt.Println("succeed")
}
以下のコマンドを使用してプログラムを実行します。
go run channel1.go
プログラムの出力は以下の通りです。
fatal error: all goroutines are asleep - deadlock!
チャネルが既に満杯であるため、プログラムがデッドロックすることがわかります。
この問題を解決するには、まずチャネルからデータを取り出すことができます。channel.go
に以下のコードを書きます。
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10
fmt.Println("Data extracted:", <-ch)
ch <- 20
fmt.Println("succeed")
}
以下のコマンドを使用してプログラムを実行します。
go run channel.go
プログラムの出力は以下の通りです。
Data extracted: 10
succeed
取り出し、使用し、再び取り出し、再び使用するという戦略を採用することで、限られた容量のチャネルを継続的に使用することができます。