登录
首页 >  Golang >  Go问答

立即停止所有递归产生的goroutine

来源:stackoverflow

时间:2024-03-01 20:30:25 252浏览 收藏

各位小伙伴们,大家好呀!看看今天我又给各位带来了什么文章?本文标题《立即停止所有递归产生的goroutine》,很明显是关于Golang的文章哈哈哈,其中内容主要会涉及到等等,如果能帮到你,觉得很不错的话,欢迎各位多多点评和分享!

问题内容

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

我想知道如何退出所有这些 goroutine——即关闭 ch——一旦我发现某种类型的节点或某些其他条件已满足。我尝试使用 quit 通道,该通道会在生成新的 goroutines 之前进行轮询,如果收到值,则关闭 ch ,但这会导致竞争条件,其中一些 goroutine 尝试在刚刚被另一个 goroutine 关闭的通道上发送。我正在考虑使用互斥体,但它似乎不优雅并且违背了用互斥体保护通道的精神。有没有一种惯用的方法可以使用渠道来做到这一点?如果没有的话,有什么办法吗?任何意见表示赞赏!


解决方案


context 包提供类似的功能。使用 context.context 和一些 go 风格的模式,您可以实现您所需要的。

首先,您可以查看这篇文章,以便更好地了解使用 context 取消的感觉:https://www.sohamkamani.com/blog/golang/2018-06-17-golang-using-context-cancellation/

另外请务必查看官方 godoc:https://golang.org/pkg/context/

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

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
    }
}

到这里,我们也就讲完了《立即停止所有递归产生的goroutine》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于的知识点!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>