我正在嘗試重現一個問題,并使用以下代碼找到了最小用例。如果我關閉所有通道(繞過 i == 0 測試),一切都會按預期進行。Wg 狀態遞減并觸發完成,主要退出正常。當我跳過關閉這些通道之一(故意)時,我希望主例程等待,而在這種情況下等待組信號量將無限期阻塞。相反,我收到了一個錯誤:“致命錯誤:所有 goroutine 都睡著了 - 死鎖!”。這是為什么?我一定錯過了一些基本的東西,還是運行時過于熱心了?package mainimport ( "fmt" "sync")const N int = 4func main() { done := make(chan struct{}) defer close(done) fmt.Println("Beginning...") chans := make([]chan int, N) var wg sync.WaitGroup for i := 0; i < N; i++ { wg.Add(1) chans[i] = make(chan int) go func(i int) { // p0 defer wg.Done() for m := range chans[i] { fmt.Println("Received ", m) } fmt.Println("Ending p", i) }(i) } go func() { wg.Wait() done <- struct{}{} // signal main that we are done }() for i := 0; i < N; i++ { fmt.Println("Closing c", i) if i != 0 { // Skip #0 so wg doesn't reach '0' close(chans[i]) } } <-done // wait to receive signal from anonymous join function fmt.Println("Ending.")}更新:我編輯了代碼以避免競爭條件。仍然收到此錯誤。if i != 0之所以存在,是因為它是故意的。我希望 wg.Wait 永遠阻塞(其信號量永遠不會達到 0。)為什么我不能這樣做?這似乎與我在其他地方<-done沒有匹配的情況下使用done <- struct{}{}一樣。在這種情況下編譯器也會抱怨嗎?
2 回答

小怪獸愛吃肉
TA貢獻1852條經驗 獲得超1個贊
這是發生了什么:
第一個
go func(i int) {
goroutine 沒有退出,因為chans[0]
沒有關閉。因為 goroutine 沒有退出,
wg.Done
也沒有被調用。wg.Wait()
由于上一點,對永遠阻塞的調用。Main 永遠阻塞,因為信號沒有發送到
done
。
您可以通過刪除 來解決死鎖if i != 0 {
,但還有另一個問題。等待組有一場比賽。有可能在調用 wg.Add(1) 之前調用 wg.Done()。在啟動 goroutine 之前調用 wg.Add() 以避免競爭。

守著一只汪
TA貢獻1872條經驗 獲得超4個贊
if
你的 for 循環中的語句不會讓最后一個通道關閉,所以你goroutine
要等待發生的事情,chans[i]
這將阻止defer wg.Done()
永遠發生的事情,這反過來永遠不會讓wg.Wait()
完成,THENNNNN 永遠不會讓done <- struct{}{}
得到信號
簡而言之,您if statement
的循環并沒有關閉最后一個通道并導致死鎖,因為沒有人無能為力。
正如@CodingPickle 確實指出的那樣,將您移動wg.Add(1)
到您的開頭for loop
以防止任何競爭條件
http://play.golang.org/p/j1D5LZGUhd
- 2 回答
- 0 關注
- 232 瀏覽
添加回答
舉報
0/150
提交
取消