この些細な状況については簡単な説明があると確信していますが、go
同時実行モデルは初めてです。
この例を実行すると
package main
import "fmt"
func main() {
c := make(chan int)
c <- 1
fmt.Println(<-c)
}
私はこのエラーを受け取ります:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/tarrsalah/src/go/src/github.com/tarrsalah/tour.golang.org/65.go:8 +0x52
exit status 2
どうして ?
ラッピングc <-
内のgoroutine
は、サンプルを期待どおりに実行します
package main
import "fmt"
func main() {
c := make(chan int)
go func(){
c <- 1
}()
fmt.Println(<-c)
}
繰り返しますが、なぜですか?
デッドロックを解消してコードを修正する方法だけでなく、詳細な説明が必要です。
ドキュメント から:
チャネルがバッファされていない場合、送信者は受信者が値を受信するまでブロックします。チャネルにバッファがある場合、送信者は値がバッファにコピーされるまでのみブロックします。バッファがいっぱいの場合、これは、一部の受信者が値を取得するまで待機することを意味します。
そうでなければ言った:
この行
c <- 1
チャネルがバッファリングされていないためブロックします。値を受け取るゴルーチンは他にないため、状況は解決できません。これはデッドロックです。
チャンネル作成を次のように変更することで、ブロックしないようにすることができます
c := make(chan int, 1)
そのため、ブロックする前にチャネル内の1つのアイテムのためのスペースがあります。
しかし、それは並行性の問題ではありません。通常、中に入れたものを処理するために他のゴルーチンのないチャンネルを使用することはありません。次のような受信ゴルーチンを定義できます。
func main() {
c := make(chan int)
go func() {
fmt.Println("received:", <-c)
}()
c <- 1
}
バッファリングされていないチャネルでは、データの受信を待機しているレシーバが必要になるまでチャネルへの書き込みは行われません。これは、以下の例で意味します
func main(){
ch := make(chan int)
ch <- 10 /* Main routine is Blocked, because there is no routine to receive the value */
<- ch
}
さて、他のgoルーチンがある場合、同じ原則が適用されます
func main(){
ch :=make(chan int)
go task(ch)
ch <-10
}
func task(ch chan int){
<- ch
}
これは、taskルーチンがデータが消費されるのを待ってから、バッファリングされていないチャネルへの書き込みが発生するため、機能します。
より明確にするために、main関数の2番目と3番目のステートメントの順序を入れ替えましょう。
func main(){
ch := make(chan int)
ch <- 10 /*Blocked: No routine is waiting for the data to be consumed from the channel */
go task(ch)
}
これはデッドロックにつながります
つまり、バッファリングされていないチャネルへの書き込みは、チャネルからの読み取りを待機しているルーチンがある場合にのみ発生します。そうでない場合、書き込み操作は永久にブロックされ、デッドロックにつながります。
[〜#〜] note [〜#〜]:バッファリングされたチャネルにも同じ概念が適用されますが、送信者はバッファがいっぱいになるまでブロックされません。操作。
したがって、サイズ1のバッファリングされたチャネルがある場合、上記のコードは機能します
func main(){
ch := make(chan int, 1) /*channel of size 1 */
ch <-10 /* Not blocked: can put the value in channel buffer */
<- ch
}
しかし、上記の例にさらに値を書き込むと、デッドロックが発生します
func main(){
ch := make(chan int, 1) /*channel Buffer size 1 */
ch <- 10
ch <- 20 /*Blocked: Because Buffer size is already full and no one is waiting to recieve the Data from channel */
<- ch
<- ch
}
この回答では、チャネルとゴルーチンの観点からgoがどのように機能するかを少し覗くことができるエラーメッセージを説明しようとします
最初の例は次のとおりです。
package main
import "fmt"
func main() {
c := make(chan int)
c <- 1
fmt.Println(<-c)
}
エラーメッセージは次のとおりです。
fatal error: all goroutines are asleep - deadlock!
コードにはゴルーチンはまったくありません(ところで、このエラーはコンパイル時ではなくランタイムにあります)。 goがこの行を実行するとc <- 1
、チャネル内のメッセージがどこかで受信されることを確認したい(つまり<-c
)。 Goは、この時点でチャネルが受信されるかどうかを知りません。 goは、次のいずれかが発生するまで、実行中のゴルーチンが終了するのを待ちます。
ケース#1では、goは上記のメッセージでエラーを出力します。これは、goroutineがチャネルを受信し、チャネルを必要とする方法がないことを知っているためです。
ケース#2では、プログラムは続行されます。これで、このチャネルが受信されたことがわかります。これは、OPの例で成功したケースを説明しています。
messages:= make(chan string、2)//-最大2つの値をバッファリングする文字列のチャネル。
チャネルでの基本的な送受信がブロックされています。ただし、select
句をdefault
句とともに使用して、非ブロッキング送信、受信、さらには非ブロッキングのマルチウェイselect
sを実装できます。 。