疑問
表題のとおり。
なぜバッファなしのチャンネルに対して、メイン処理で送受信をおこなうとデッドロックが発生するのか。
バッファリングなし
チャンネルはデフォルトではバッファを持たない。
これが何を意味するかというと、並行処理で走る「レシーバー」の受信準備ができているときにだけ、チャンネルに値を送信できるということだ。
例
package main
import "fmt"
func main() {
// バッファを持たないチャンネルを作る
messages := make(chan string)
// 別の goroutine で受信準備をする
go func() { fmt.Println(<-messages) }()
// メイン処理 ( メインの goroutine ) でチャンネルに値を送信する
messages <- "value"
}
バッファリングあり
だがチャンネルにバッファを持たせた場合、レシーバーがいない場合でも、チャンネルに指定個数分の値を持たせることが出来る。
例
Go by Example: Channel Buffering からほぼそのまま
package main
import "fmt"
func main() {
// 2個のバッファを持ったチャンネルを作成
messages := make(chan string, 2)
// レシーバーは存在しない状態だが、チャンネルは値を二個まで持てる
messages <- "buffered"
messages <- "channel"
// 同じくメイン処理で、チャンネルから二回の受信をおこなえる
fmt.Println(<-messages)
fmt.Println(<-messages)
}
まったく実用的な例ではないが、別の goroutine を走らせなくても、メイン処理だけでチャンネルへの値の送受信が出来ているのが分かる。
(バッファ個数を指定しないと、チャンネルに値を送信しようとした時点でデッドロックが発生し、エラーが起こる)
疑問
ではなぜ、非メインの goroutine の中で送信だけをおこなう場合、デッドロックが発生しないのか。
非メインの goroutine での送信は、他の goroutine の受信を待つ
っぽい。たぶん。
というわけではないことが後ほど分かった。
その後書いた記事 — Go言語—チャンネル、バッファ、ブロック、デッドロックを理解する ( そして楽しいgoroutine ) - Qiita
package main
import "fmt"
import "time"
func main() {
communicate_channel := make(chan bool)
orphan_channel := make(chan bool)
// チャンネルにメッセージを送信しようとする
// あとでメイン処理で受信がおこなわれる
go func() {
fmt.Println("Before send message to communicate channel") // Run
communicate_channel <- true // Run
fmt.Println("After sent message to communicate channel") // Run
}()
// もうひとつのチャンネルにメッセージを送信しようとする
// だがどこでも受信はおこなわれないので、実際の送信はおこなわれない (デッドロックも起こらない)
go func() {
fmt.Println("Before send message to orphan channel") // Run
orphan_channel <- true // Wait
fmt.Println("After sent message to orphhan channel") // Not run
}()
// 片方のチャンネルだけからメッセージを受信する
<-communicate_channel
time.Sleep(300 * time.Millisecond)
}
感想
- あくまでチャンネルは、goroutine間の値の送受信のためのものなので、メイン処理だけで使おうとしてもエラーが起るのは当たり前。(という理解)
- 最初、チャンネルが持つバッファの位置づけというか、役割がまったく分からなかった。
- なぜメイン処理だけでチャンネルに値を送受信しようとすると、デッドロックが発生するのか。
- なぜ (go 文を使うなどして) goroutine で同じことをした場合は、同じ問題が発生しないのか。
- 並行処理でレシーバーを走らせた場合は、バッファがなくても全く問題ないように見えたので、さらに謎が深かった。
- この部分を理解するのに相当苦労したが、ふりかえれば全部 Go Example に書かれていた。
環境
- go version go1.10.3 darwin/amd64
参考
チャットメンバー募集
何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。