1 回答

TA貢獻1895條經驗 獲得超3個贊
每次調用時recv1Block()
,它都會創建一個新通道并啟動一個后臺 goroutine,該 goroutine 會嘗試從中讀取所有數據。由于您是在循環中調用它,因此您將有很多東西都在嘗試使用通道中的數據;由于通道永遠不會關閉,所有的 goroutines 將永遠運行。
作為練習,您可以嘗試更改代碼以傳遞 achan int
而不是 a chan struct{}
,并編寫一系列序列號,并在最終收到時將它們打印出來。像這樣的序列是有效的:
run
在 goroutine #1 上調用recv1Block()
。recv1Block()
在 GR#1 上生成 GR#2,并返回通道#2。run
在 GR#1 上阻止在通道#2 上接收。recv1Block()
在 GR#2 上讀取0
自w.c1
.recv1Block()
在 GR#2 上寫入0
通道#2(run
在 GR#1 上準備好讀取)。recv1Block()
在 GR#2 上讀取1
自w.c1
.recv1Block()
GR#2 上想要寫入0
通道#2 但阻塞了。run
GR#1 上的喚醒,并接收到0
。run
在 GR#1 電話上recv1Block()
。recv1Block()
在 GR#1 上生成 GR#3,并返回通道 #3。recv1Block()
在 GR#3 上讀取2
自w.c1
....
請注意,此序列中的值 1 永遠不會被寫入任何地方,實際上沒有任何東西可以讀取它。
這里的簡單解決方案是不要在循環中調用通道創建函數:
func (w *waiter) runBlock(wg *sync.WaitGroup) {
defer wg.Done()
ch1 := w.recv1Block()
ch2 := w.recv2()
for {
select {
case _, ok := <-ch1:
if !ok {
return
}
w.count++
case <-ch2:
}
}
完成頻道后關閉頻道也是標準做法。這將終止一個for ... range ch循環,并且它看起來對select語句是可讀的。在您的頂級生成器函數中:
for i := 0; i < w.limit; i++ {
w.ch1 <- struct{}{}
}
close(w.ch1)
在您的“復制頻道”功能中:
func (w *waiter) recv1Block() chan struct{} {
ch := make(chan struct{})
go func() {
for m := range w.ch1 {
ch <- m
}
close(ch)
}()
return ch
}
這也意味著您不需要通過“航位推算”運行主循環,期望它正好產生 100 個項目然后停止;只要它的頻道退出,你就可以停止。我上面顯示的消費者循環就是這樣做的。
- 1 回答
- 0 關注
- 156 瀏覽
添加回答
舉報