因此,讓我們看看你的來源真正發生了什麼。你有兩個 goroutines(有兩個以上,但我們將專注於明確的),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#
現在你可以gue ss爲什麼你會陷入僵局。在進行中,無緩衝通道將阻止,直到通道的另一端發生某些事情,無論您是發送還是接收。因此c <- 10
會阻塞,直到從c
的另一端讀取某些內容爲止,但您爲此所用的參數在2秒前已經退出畫面。因此,c
永遠是塊,並且因爲main
是最後一個goroutine,你會得到一個死鎖。
如何預防?使用頻道時,請確保每個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'例程)只會在從'c'讀入內容時纔會發生,對吧? –
沒錯,但爲了清理一下,它保持** goroutine **運行。 – AJPennster