如何判断一个 goroutine 是否成功或所有 goroutines 都已完成?

我正在尝试使用 DFS 通过检查图中每个节点的循环来检查循环图。


我想为每个节点运行一个 goroutine,并在检测到第一个循环或没有循环时终止。


在检测到第一个周期时终止似乎很好,但我无法将其与不再有节点的情况混合使用。


下面是一个似乎有效的实现,但对我来说看起来很糟糕,我一直在寻找更好的实现。我基本上使用缓冲通道并在每次我没有得到一个循环时递增一个计数器,并在计数器达到底层图形的大小时终止。


使用计数器来确定我们何时完成对我来说似乎不合时宜。或者,更确切地说,很高兴知道在 Go 中是否有更惯用的方法来执行此操作。


func (c *cycleDet) hasCycle() bool {

    adj := c.b // adjacency list representation of the unerlying graph

    hasCycles := make(chan bool, len(adj))


    for node := range adj {

        go func(no nodeID, co chan<- bool) {

            visited := make(map[nodeID]struct{})

            // c.nodeHasCycle will use a recursive implementation of DFS to

            // find out if the node no leads to a cycle.

            if c.nodeHasCycle(no, visited) {

                co <- true

                return

            }

            co <- false

        }(node, hasCycles)

    }


    var i int

    for {

        select {

        case v := <-hasCycles:

            if v {

                fmt.Println("got cycle")

                return true

            }

            if i == len(c.b) {

                return false

            }

            i++

        }

    }

}

我还遇到了另一个推荐使用“观察者”goroutine 的 SO 帖子(尽管我找不到 SO 帖子)。我不确定那会是什么样子,但想象一下它会WaitGroup像这样与 s 混合:


func (c *cycleDet) hasCycle() bool {

    hasCycle := make(chan bool)

    done := make(chan struct{})

    var wg sync.WaitGroup


    adj := c.b // adjacency list representation of the unerlying graph

    for node := range adj {

        go func(no nodeID, co chan<- bool) {

            // Use wg to synchronize termination of read-only goroutines.

            wg.Add(1)

            defer wg.Done()


            visited := make(map[nodeID]struct{})

            // c.nodeHasCycle will use a recursive implementation of DFS to

            // find out if the node no leads to a cycle.

            if c.nodeHasCycle(no, visited) {

                co <- true

                return

            }

        }(node, hasCycle)

    }


然而,这种方法让我担心,因为我需要睡觉让观察者只在第一个WaitGroup计数器递增后才开始等待,这似乎比第一个解决方案脆弱得多。


慕勒3428872
浏览 101回答 1
1回答

HUH函数

你是对的,这WaitGroup可能就是你想要的。但是,您没有正确使用它。首先,您需要在wg.Add(1)调用wg.Done(). 其次,调用wg.Wait()块直到等待组中的所有 go routines 完成执行,所以你不希望它并发执行。至于短路,确实没有什么好办法。我的建议是使用上下文。在这种情况下,您必须做的一件事是将上下文连接到您的调用中,nodeHasCycle如果您想要真正的短路行为。修复你的代码,我们有:func (c *cycleDet) hasCycle() bool {&nbsp; &nbsp; ctx, cancel := context.WithCancel(context.Background())&nbsp; &nbsp; hasCycle := make(chan bool)&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; adj := c.b // adjacency list representation of the unerlying graph&nbsp; &nbsp; for node := range adj {&nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; go func(ctx context.Context, no nodeID, co chan<- bool, cancel context.CancelFunc) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Use wg to synchronize termination of read-only goroutines.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case <-ctx.Done():&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; visited := make(map[nodeID]struct{})&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // c.nodeHasCycle will use a recursive implementation of DFS to&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // find out if the node no leads to a cycle.&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if c.nodeHasCycle(ctx, no, visited) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; co <- true&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; cancel()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }(ctx, node, hasCycle, cancel)&nbsp; &nbsp; }&nbsp; &nbsp; // Observer goroutine to notify when wg is done waiting.&nbsp; &nbsp; time.Sleep(100 * time.Millisecond)&nbsp; &nbsp; wg.Wait()&nbsp; &nbsp; defer cancel()&nbsp; &nbsp; select {&nbsp; &nbsp; case <-hasCycle:&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("got a cycle")&nbsp; &nbsp; &nbsp; &nbsp; return true&nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("no cycle detected")&nbsp; &nbsp; &nbsp; &nbsp; return false&nbsp; &nbsp; }}通过这种方式设置,您可以确保对 go-routine 的所有调用都将运行,除非找到一个循环,在这种情况下,不会调用其他 go-routes 并且,如果您添加逻辑以检查是否取消nodeHasSycle,则您也可以停止对任何正在运行的调用的执行。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go