3 回答

TA貢獻1775條經驗 獲得超11個贊
所以,讓我們看看你的源代碼中到底發生了什么。你有兩個goroutine(不止兩個,但我們將專注于顯式的),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,但是您為此擁有的 goroutine 已在 2 秒前從圖片中刪除。因此,c永遠阻塞,并且由于main是最后一個 goroutine,你會得到一個死鎖。
如何預防?使用頻道時,請確保receive在頻道的另一端始終有一個send.
在這種情況下使用緩沖通道可以作為一種快速修復,但可能會在更大的存儲庫中引發潛在的陷阱。例如,假設您c隨后寫入了更多數據并go readFromChannel(c, time.After(time.Duration(2)*time.Second))再次運行。你可能會看到:
Read D1
Read D2
或者
TIMED OUT
Read D1
完全基于機會。這可能不是您想要的行為。
以下是我解決死鎖的方法:
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()
}

TA貢獻1848條經驗 獲得超10個贊
你有一個無緩沖的頻道。根據文檔:
如果通道沒有緩沖,發送方會阻塞,直到接收方收到該值。如果通道有緩沖區,發送方只會阻塞,直到值被復制到緩沖區
通過將通道更改為緩沖,我們可以避免死鎖。
c := make(chan int, 10) // holds 10 ints
我還建議閱讀https://golang.org/doc/effective_go.html#channels,里面有一些與頻道相關的好東西。

TA貢獻1802條經驗 獲得超10個贊
這是一個較老的問題,但我自己正在深入學習渠道并在這里找到了這個。
我認為您只需要在完成發送后關閉頻道嗎?
代碼:
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
close(c) // <- CLOSE IT HERE
wg.Wait()
}
- 3 回答
- 0 關注
- 171 瀏覽
添加回答
舉報