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

关闭由通道链接的 goroutine 链的优雅方法是什么?

关闭由通道链接的 goroutine 链的优雅方法是什么?

Go
慕的地8271018 2021-12-07 10:44:48
我是围棋学习者。为了更好地理解通道和 goroutine 的关心和馈送,我试图构建一个 Eratosthenes 筛,作为一组通过通道连接到管道中的 goroutine。这是我到目前为止所拥有的:// esieve implements a Sieve of Eratosthenes// as a series of channels connected together// by goroutinespackage mainimport "fmt"func sieve(mine int, inch chan int) {    start := true                        // First-number switch    ouch := make(chan int)               // Output channel for this instance    fmt.Printf("%v\n", mine)             // Print this instance's prime    for next := <-inch; next > 0; next = <-inch {  // Read input channel        fmt.Printf("%v <- %v\n",mine,next)         // (Trace)        if (next % mine) > 0 {                     // Divisible by my prime?            if start {                   // No; is it the first number through?                 go sieve(next, ouch)     // First number - create instance for it                start = false            // First time done            } else {                     // Not first time                ouch <- next             // Pass it to the next instance            }        }    }}func main() {    lim := 30                     // Let's do up to 30    fmt.Printf("%v\n", 2)         // Treat 2 as a special case    ouch := make(chan int)        // Create the first segment of the pipe    go sieve(3, ouch)             // Create the instance for '3'    for prime := 3; prime < lim; prime += 2 { // Generate 3, 5, ...        fmt.Printf("Send %v\n", prime)        // Trace        ouch <- prime                         // Send it down the pipe    }}就目前而言,它运行良好。但是,当我完成主循环时,main在sieve实例管道中的所有数字传播到最后之前退出。使主例程等待一组 goroutines(它只“知道”第一个)完成的最简单、最优雅或普遍接受的方法是什么?
查看完整描述

2 回答

?
波斯汪

TA贡献1811条经验 获得超4个赞

并且在一切都完成后出现了一些涉及死锁的错误启动之后,这是我的解决方案正常工作:


// esieve implements a Sieve of Eratosthenes

// as a series of channels connected together

// by goroutines

package main


import "fmt"


func sieve(mine int,                  // This instance's own prime

           inch chan int,             // Input channel from lower primes

           done chan int,             // Channel for signalling shutdown

           count int) {               // Number of primes - counter

    start := true                     // First-number switch

    ouch := make(chan int)            // Output channel, this instance

    fmt.Printf("%v ", mine)           // Print this instance's prime

    for next := <-inch; next > 0; next = <-inch { // Read input channel

        if (next % mine) > 0 {        // Divisible by my prime?

            if start {                // No; first time through?

                go sieve(next, ouch, done, count+1) // First number,

                                                    // create instance for it

                start = false         // First time done

            } else {                  // Not first time

                ouch <- next          // Pass to next instance

            }

        }

    }

    if start {                        // Just starting?

        close(done)                   // Yes - we're last in pipe - signal done

        print("\n",count," primes\n") // Number of primes/goroutines

    } else {

        close(ouch)                   // No - send the signal down the pipe

    }

}


func main() {

    lim := 100                        // Let's do up to 100

    done := make(chan int)            // Create the done return channel

    ouch := make(chan int)            // Create the first segment of the pipe

    go sieve(2, ouch, done, 1)        // Create the first instance for '2'

    for prime := 3; prime < lim; prime += 1 { // Generate odd numbers

        ouch <- prime                         // Send numbers down the pipe

    }

    close(ouch)                       // Send the done signal down the pipe

    <- done                           // and wait for it to come back

}

与许多其他语言相比,Go 对于这种编程的优雅和简单给我留下了深刻的印象。当然,疣是我自己要求的。


如果在这里合适,我欢迎批评性评论。


查看完整回答
反对 回复 2021-12-07
?
狐的传说

TA贡献1804条经验 获得超3个赞

至于您的标题问题,当您不再需要它们时杀死工作程序 goroutine: 您可以使用 Done 成语。从封闭通道读取产生零值。


创建一个新频道done。当从这个通道读取成功时,goroutines 知道他们应该退出。当您拥有所需的所有值时,关闭主通道。


检查您是否可以从 channel 读取done,并通过返回退出,或者在可用时从 next 读取。这部分替换了nextfor 循环中的赋值:


select {

case <-done:

return

case next = <- inch:

}

在通道上测距也有效,因为关闭该通道退出循环。


至于反过来,你的身体问题,等待一组goroutines完成:


使用sync.WaitGroup.


var wg sync.WaitGroup

wg.Add(goroutineCount)

当每个 goroutine 完成时:


wg.Done()

或者使用延迟:


defer wg.Done()

等待所有这些都报告为完成:


wg.Wait()

在您的示例中,只需wg.Add(1)在启动新 goroutine 时调用,然后再调用wg.Done()和返回即可。只要您只达到零一次,就会wg.Wait()按预期工作,所以wg.Add(1)在wg.Done.


查看完整回答
反对 回复 2021-12-07
  • 2 回答
  • 0 关注
  • 138 浏览
慕课专栏
更多

添加回答

举报

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