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条经验 获得超7个赞
问题是没有任何东西可以关闭结果通道,但范围循环仅在关闭时退出。我简化了您的代码来说明这一点并提出了一个解决方案 - 基本上使用 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 关注
- 141 浏览
添加回答
举报