3 回答

TA貢獻1804條經驗 獲得超7個贊
while tag := xml.getNextTag() {
wg.Add(1)
go parseTagText(chan, wg, tag.text)
// consume a channel message if available
select {
case msg := <-chan:
// do something with msg
default:
}
}
// reading tags finished, wait for running goroutines, consume what's left on the channel
for msg := range chan {
// do something with msg
}
// Sometimes this point is never reached, I get a deadlock
wg.Wait()
----
func parseTagText(chan, wg, tag.text) {
defer wg.Done()
// parse tag.text
chan <- whatever // just inform that the text has been parsed
}
完整代碼: https:
//play.golang.org/p/0t2EqptJBXE

TA貢獻1810條經驗 獲得超4個贊
在 Go Playground 上的完整示例中,您:
創建一個通道(第 39 行,
results := make(chan langs)
)和一個等待組(第 40 行,var wait sync.WaitGroup
)。到目前為止,一切都很好。循環:在循環中,有時會衍生出一個任務:
if ...various conditions... { wait.Add(1) go parseTerm(results, &wait, text) }
在循環中,有時會從通道進行非阻塞讀取(如您的問題所示)。這里也沒有問題。但...
在循環結束時,使用:
for res := range results { ... }
在所有作家完成后,無需精確地調用
close(results)
一個地方。此循環使用從通道的阻塞讀取。只要某個 writer goroutine 仍在運行,阻塞讀取就可以阻塞,而不會導致整個系統停止,但是當最后一個 writer 完成寫入并退出時,就沒有剩余的 writer goroutine 了。任何其他剩余的 goroutine 可能會拯救你,但沒有。
由于您使用var wait
正確(在正確的位置添加 1,并Done()
在 writer 中的正確位置調用),解決方案是再添加一個 goroutine,它將拯救您:
go func() { wait.Wait() close(results) }()
您應該在進入循環之前關閉這個救援 goroutine for res := range results
。(如果您更早地將其分離,它可能會wait
很快看到變量計數減至零,就在它通過分離另一個 再次計數之前parseTerm
。)
這個匿名函數將阻塞在wait
變量的Wait()
函數中,直到最后一個 writer Goroutine 調用了 Final wait.Done()
,這將解除對這個Goroutine 的阻塞。然后這個 goroutine 將調用close(results)
,這將安排goroutinefor
中的循環main
完成,從而解鎖該 goroutine。當這個 goroutine(救援者)返回并因此終止時,不再有救援者,但我們不再需要任何救援者。
(這個主代碼然后wait.Wait()
不必要地調用:因為直到新的goroutine中的已經解除阻塞for
才終止,我們知道下一個將立即返回。所以我們可以放棄第二個調用,盡管保留它是無害的。)wait.Wait()
wait.Wait()

TA貢獻1827條經驗 獲得超8個贊
問題是沒有任何東西可以關閉結果通道,但范圍循環僅在關閉時退出。我簡化了您的代碼來說明這一點并提出了一個解決方案 - 基本上使用 goroutine 中的數據:
// This is our producer
func foo(i int, ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
ch <- i
fmt.Println(i, "done")
}
// This is our consumer - it uses a different WG to signal it's done
func consumeData(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
for x := range ch {
fmt.Println(x)
}
fmt.Println("ALL DONE")
}
func main() {
ch := make(chan int)
wg := sync.WaitGroup{}
// create the producers
for i := 0; i < 10; i++ {
wg.Add(1)
go foo(i, ch, &wg)
}
// create the consumer on a different goroutine, and sync using another WG
consumeWg := sync.WaitGroup{}
consumeWg.Add(1)
go consumeData(ch,&consumeWg)
wg.Wait() // <<<< means that the producers are done
close(ch) // << Signal the consumer to exit
consumeWg.Wait() // << Wait for the consumer to exit
}
- 3 回答
- 0 關注
- 202 瀏覽
添加回答
舉報