チャネル(channel)はゴルーチン(goroutine)間で値を受け渡しすることができる通信の仕組みで、チャネルは要素型と呼ばれる特定の型の値のみ送信できる。
今回は、「バッファなしチャネル」と異なる点を中心に「バッファありチャネル」についてまとめていく。
チャネルの基本的なことは「【Go言語】「バッファなしチャネル(channel)」を理解したいんじゃ!!」を参照。
バッファありチャネル(channel)とは?
バッファありチャネルは、要素のキューを持っている。キューのサイズはチャネルを生成するときにmake
に渡される容量の引数によって決まる。
チャネルに送信するとき、キューの最後に値を追加し、受信するときはキューの先頭から値を取り除く。
バッファありチャネルの宣言、初期化
「バッファありチャネル」の場合、make
の2つ目の引数にバッファのサイズを渡す。
例えば、3つのint値を保持できるバッファありチャネルの場合
var ch chan int ch = make(chan int, 3) // 関数内であれば、 := を使ってもOK ch := make(chan int, 3)
チャネルのブロック
バッファありチャネルの場合、
ポイント
- チャネルが「一杯」なら、送信操作は、別のゴルーチンによる受信操作によって「空き」ができるまで待たされる(ブロック)
→ チャネルに「空き」があれば、送信操作は待たされない - チャネルが「空」なら、受信操作は別のゴルーチンが値を送信するまで待たされる(ブロック)
簡単なサンプルを書いてみる。
3つのint値を保持できるバッファありチャネルに対して
- 5つのint値を送信する無名ゴルーチン
- 受信するメインゴルーチン
を用意。
実行結果と合わせてみるとブロックの様子がわかりやすいはず。
package main import ( "fmt" "time" ) func main() { ch := make(chan int, 3) go func() { defer close(ch) for i := 0; i < 5; i++ { ch <- i fmt.Printf("sent %d\n", i) } }() time.Sleep(3 * time.Second) // 出力をわかりやすくすため、スリープ for v := range ch { fmt.Printf("received value: %d\n", v) time.Sleep(1 * time.Second) // 出力をわかりやすくすため、スリープ } }
この実行結果は、
$ go run main.go sent 0 sent 1 sent 2 // ← ここでチャネルは一杯。チャネルが一杯になるまでは、受信操作が実行されなくても送信できる received value: 0 // ← 1つ受信した(取り出した)ので、チャネルに空きができる sent 3 // ← チャネルに空きができたので、送信。そして、また一杯になる received value: 1 // ← 1つ受信した(取り出した)ので、チャネルに空きができる sent 4 // ← チャネルに空きができたので、送信 received value: 2 received value: 3 received value: 4
実行結果のコメントに書いたように、チャネルに空きがあるうちは、待たされることなく送信でき、チャネルが一杯になったら待機しているのがわかる。
バッファありチャネル まとめ
まとめ
make
の第二引数にバッファサイズを指定して生成- チャネルが「一杯」なら、送信操作は、別のゴルーチンによる受信操作によって「空き」ができるまで待たされる
- チャネルに「空き」があるなら、送信操作は待たされることなく実行される
- チャネルが「空」なら、受信操作は別のゴルーチンが値を送信するまで待たされる
参考図書
Tour of Goを終えた人にオススメです!!