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

我有一个函数可以递归地生成 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 doc

ch := 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 关闭的通道上发送。我正在考虑使用互斥锁,但它似乎不够优雅并且违背了使用互斥锁保护通道的精神。有没有一种惯用的方法可以使用频道来做到这一点?如果没有,有什么办法吗?任何输入表示赞赏!


胡子哥哥
浏览 120回答 1
1回答

桃花长相依

该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    }}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go