単一選択ケースでの連鎖チャネル操作: 動作のデコード
同時および非同期プログラムの設計を追求する中で、Go の選択構造は以下を提供します。チャンネルを多重化するための強力なツールです。ただし、単一の選択ケース内で複数の操作を組み合わせると、予期しない結果が発生することがよくあります。
次のシナリオを考えてみましょう: 2 つのチャネル A と B が、異なる時間間隔 (A では 10 ミリ秒、A では 1 秒) でメッセージを送信します。 B)。 select を使用して両方のチャネルをリッスンし、受信した値をファンイン チャネルに転送します。
func main() { ch := fanIn(talk("A", 10), talk("B", 1000)) for i := 0; i < 10; i++ { fmt.Printf("%q\n", <-ch) } fmt.Printf("Done\n") }
期待される結果は次のとおりです。
"A 0" "B 0" "A 1" "A 2" "A 3" "A 4" "B 1" "B 2" "B 3" "B 4" Done
ただし、select を変更すると、連鎖チャネル操作を使用する場合:
select { case ch <- <-input1: case ch <- <-input2: }
特有の現象が観察されます。動作:
"B 0" "A 1" "B 2" "A 3" "A 4" fatal error: all goroutines are asleep - deadlock!
舞台裏
この動作を理解する鍵は、選択されたケース内のチャネル操作のノンブロッキングの性質にあります。一般的な選択ケースでは、非ブロッキングにできるのは 1 つのチャネル操作 (読み取りまたは書き込み) のみです。
チェーンされたチャネル操作を使用すると、1 つのケース内で複数のチャネル操作を効果的に試行することになります。最初の操作は常にブロックですが、後続の操作は非ブロックです。
変更されたコードでは、最初の操作は input1 からの値の受信をブロックします。値を受信した後、それを ch チャネルにノンブロッキングで書き込もうとします。ただし、ch チャネルの受信側が値を受け入れる準備ができていない場合、書き込み操作は失敗します。
連鎖反応
失敗した書き込み操作は、選択ケースを停止します。代わりに、現在唯一実行可能なケースである 2 番目のケースに進みます。これにより、潜在的なデッドロック シナリオが発生します。
時間の経過とともに、両方のチャネルから複数の値が受信されますが、書き込みの失敗によりファンイン チャネルに転送されなくなります。その結果、ファンイン チャネルは最終的に空になり、それ以上の値を受信できないためデッドロックが発生します。
問題の解決
この問題を回避するには、これは、選択されたケース内のチャネル操作が確実にシリアルに実行されるようにするために重要です。これは、一時変数を使用して受信した値を保存し、書き込み操作を選択ケースの外側の別のステートメントとして実行することで実現できます。
var msg string select { case msg = <-input1: case msg = <-input2: } ch <- msg
以上がGo の「select」ケースでチェーンされたチャネル操作がデッドロックを引き起こすのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。