立即退出所有递归生成的goroutine
我有一个递归生成goroutines以遍历DOM树的函数,将它们找到的节点放入它们之间共享的通道中立即退出所有递归生成的goroutine,go,Go,我有一个递归生成goroutines以遍历DOM树的函数,将它们找到的节点放入它们之间共享的通道中 import ( "golang.org/x/net/html" "sync" ) func walk(doc *html.Node, ch chan *html.Node) { var wg sync.WaitGroup defer close(ch) var f func(*html.Node) f = func(n *html.Node) {
import (
"golang.org/x/net/html"
"sync"
)
func walk(doc *html.Node, ch chan *html.Node) {
var wg sync.WaitGroup
defer close(ch)
var f func(*html.Node)
f = func(n *html.Node) {
defer wg.Done()
ch <- n
for c := n.FirstChild; c != nil; c = c.NextSibling {
wg.Add(1)
go f(c)
}
}
wg.Add(1)
go f(doc)
wg.Wait()
}
我想知道,一旦发现某个类型的节点或满足某些其他条件,如何退出所有这些goroutine(即关闭
ch
)。我曾尝试使用一个quit
频道,该频道在生成新的goroutine之前会被轮询,如果收到一个值,则关闭ch
,但这会导致一些goroutine尝试在刚刚被另一个goroutine关闭的频道上发送。我曾考虑过使用互斥体,但它似乎不雅观,违背了使用互斥体保护频道的精神。有没有一种惯用的方法使用频道来实现这一点?如果没有,有什么办法吗?欢迎任何意见 上下文包提供了类似的功能。使用context.context
和一些Go-esque模式,您可以实现所需的功能
首先,您可以查看本文,通过context
,获得更好的取消体验:
还要确保检查官方GoDoc:
因此,要实现此功能,您的功能应该更像:
func walk(ctx context.Context, doc *html.Node, ch chan *html.Node) {
var wg sync.WaitGroup
defer close(ch)
var f func(*html.Node)
f = func(n *html.Node) {
defer wg.Done()
ch <- n
for c := n.FirstChild; c != nil; c = c.NextSibling {
select {
case <-ctx.Done():
return // quit the function as it is cancelled
default:
wg.Add(1)
go f(c)
}
}
}
select {
case <-ctx.Done():
return // perhaps it was cancelled so quickly
default:
wg.Add(1)
go f(doc)
wg.Wait()
}
}
太好了,谢谢!我记得第一次进入围棋时,我浏览了一下上下文包,但不太了解它的用途,这很有帮助。
func walk(ctx context.Context, doc *html.Node, ch chan *html.Node) {
var wg sync.WaitGroup
defer close(ch)
var f func(*html.Node)
f = func(n *html.Node) {
defer wg.Done()
ch <- n
for c := n.FirstChild; c != nil; c = c.NextSibling {
select {
case <-ctx.Done():
return // quit the function as it is cancelled
default:
wg.Add(1)
go f(c)
}
}
}
select {
case <-ctx.Done():
return // perhaps it was cancelled so quickly
default:
wg.Add(1)
go f(doc)
wg.Wait()
}
}
// ...
ctx, cancelFunc := context.WithCancel(context.Background())
walk(ctx, doc, ch)
for value := range ch {
// ...
if someCondition {
cancelFunc()
// the for loop will automatically exit as the channel is being closed for the inside
}
}