为了账号安全,请及时绑定邮箱和手机立即绑定

立即退出所有递归生成的 goroutine

立即退出所有递归生成的 goroutine

Go
胡子哥哥 2023-06-19 15:32:30
我有一个函数可以递归地生成 goroutine 来遍历 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) {        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()}我会这样称呼// get the webpage using http// parse the html into docch := make(chan *html.Node)go walk(doc, ch)for c := range ch {    if someCondition(c) {        // do something with c        // quit all goroutines spawned by walk    }}我想知道如何退出所有这些 goroutines——即关闭ch——一旦我找到了某种类型的节点或其他一些条件已经满足。我曾尝试使用一个quit通道,该通道会在生成新的 goroutine 之前进行轮询并ch在收到值时关闭,但这会导致竞争条件,其中一些 goroutine 尝试在刚刚被另一个 goroutine 关闭的通道上发送。我正在考虑使用互斥锁,但它似乎不够优雅并且违背了使用互斥锁保护通道的精神。有没有一种惯用的方法可以使用频道来做到这一点?如果没有,有什么办法吗?任何输入表示赞赏!
查看完整描述

1 回答

?
桃花长相依

TA贡献1860条经验 获得超8个赞

context包提供了类似的功能。使用context.Context一些 Go-esque 模式,您可以实现您所需要的。

因此,要实现此功能,您的函数应该更像:

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

    }

}


查看完整回答
反对 回复 2023-06-19
  • 1 回答
  • 0 关注
  • 91 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信