だから、実際に何が起きているのかを見てみましょう。 2つののゴルーチンがあります(2つ以上ありますが、明示的なものに焦点を当てます)。main
とreadFromChannel
です。
readFromChannel
が何をするのか見てみましょう:
if channel `c` is not empty before `ti` has expired, print its contents and return, after signalling its completion to wait group.
if `ti` has expired before `c` is not empty, print "TIMED OUT" and return, after signalling its completion to wait group.
は現在メイン:
adds to waitgroup
make a channel `c`
start a goroutine `readFromChannel`
sleep for 5 seconds
send 10 to channel `c`
call wait for waitgroup
は今、(あなたのコードは/実行されない可能性があり、同時に、あなたのコードの実行の流れを通過することができます毎回この順番でこれを覚えておいてください)
1) wg.Add(1)
2) c := make(chan int)
3) go readFromChannel(c, time.After(time.Duration(2)*time.Second))
#timer ti starts#
4) time.Sleep(time.Duration(5) * time.Second)
#MAIN Goroutine begins sleep
#timer ti expires#
5) case <-ti:
6) fmt.Println("TIMED OUT")
7) wg.Done()
# readFromChannel Goroutine returns #
#MAIN Goroutine exits sleep#
8) c<-10
9) ......#DEADLOCK#
なぜあなたはデッドロックを持っているのですか?外出先では、バッファされていないチャンネルは、送信中か受信中かにかかわらず、チャンネルの反対側で何かが発生するまで、をブロックします。だからc <- 10
は、何かがc
のもう一方の端から読み込まれるまでブロックしますが、そのために持っていたゴルーチンは2秒前に画像から脱落しました。したがって、c
は永遠にブロックし、main
は最後のゴルーチンですので、デッドロックが発生します。
どうすればいいですか?チャンネルを使用する場合は、send
ごとに常にチャンネルの反対側にreceive
があることを確認してください。バッファされたチャネルを使用することもできますが、上記のコードでは、 "正しい"解決策ではありません。
はここでデッドロックのための私の修正です:
func main() {
wg.Add(1)
c := make(chan int)
go readFromChannel(c, time.After(time.Duration(2)*time.Second))
time.Sleep(time.Duration(5) * time.Second)
c <- 10
wg.Wait()
}
func readFromChannel(c chan int, ti <-chan time.Time) {
// the forloop will run forever
loop: // **
for {
select {
case x := <-c:
fmt.Println("Read", x)
break loop // breaks out of the for loop and the select **
case <-ti:
fmt.Println("TIMED OUT")
}
}
wg.Done()
}
** see this answer for details
これは、私はいくつかの時間に読んだ最も有用な答えの一つです。私がフォローしていることを確かめるために、「修正」は、タイムアウト後も受信者のチャンネルを維持するので機能します。だから 'wg.Done()'( 'main' goルーチンを終了する)は、何かが' c'から読み込まれた場合にのみ発生します。 –
そうだけど、ちょっとクリアーするには、** goroutine **の実行を保つ。 – AJPennster