バッファありチャネルとバッファなしチャネル
Go先日、ゴルーチンとチャネルを使った以下のようなプログラムを作成したところ、うまく動作しませんでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// 動作しない例 package main import ( "fmt" ) func testFunc(sendCh <-chan struct{}) { for { select { case <-sendCh: fmt.Println("Received") default: // Do nothing. } } } func main() { sendCh := make(chan struct{}) // バッファなしチャネル go testFunc(sendCh) for { select { case sendCh <- struct{}{}: fmt.Println("Send") default: // Do nothing. } } } |
メインのゴルーチンがチャネルに送信して、もうひとつのゴルーチンがチャネルから受信するだけのプログラムです。送信側と受信側の双方とも for
と select
を使ってポーリングをしています。しかし、実行してみてもまったく送信されません。
調べたところ、バッファなしチャネル(unbuffered channel)を使っているのが原因だとわかりました。バッファなしチャネルを介した通信は、送信しているゴルーチンと受信しているゴルーチンを同期させるため、送信側のゴルーチンが送信の状態で、かつ、受信側のゴルーチンが受信の状態でないと、通信が行われません。
解決するには、上のプログラムの2つのゴルーチンのどちらかの select
を使わないようにするか、以下のようにバッファありチャネル(buffered channel)を使うように変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// 動作する例 package main import ( "fmt" ) func testFunc(sendCh <-chan struct{}) { for { select { case <-sendCh: fmt.Println("Received") default: // Do nothing. } } } func main() { sendCh := make(chan struct{}, 1) // バッファありチャネル go testFunc(sendCh) for { select { case sendCh <- struct{}{}: fmt.Println("Send") default: // Do nothing. } } } |
参考文献
- 「プログラミング言語Go」 Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)