猿问

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

我是围棋学习者。为了更好地理解通道和 goroutine 的关心和馈送,我试图构建一个 Eratosthenes 筛,作为一组通过通道连接到管道中的 goroutine。


这是我到目前为止所拥有的:


// esieve implements a Sieve of Eratosthenes

// as a series of channels connected together

// by goroutines

package main


import "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(它只“知道”第一个)完成的最简单、最优雅或普遍接受的方法是什么?


慕的地8271018
浏览 139回答 2
2回答

波斯汪

并且在一切都完成后出现了一些涉及死锁的错误启动之后,这是我的解决方案正常工作:// esieve implements a Sieve of Eratosthenes// as a series of channels connected together// by goroutinespackage mainimport "fmt"func sieve(mine int,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // This instance's own prime&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;inch chan int,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Input channel from lower primes&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;done chan int,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Channel for signalling shutdown&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;count int) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Number of primes - counter&nbsp; &nbsp; start := true&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// First-number switch&nbsp; &nbsp; ouch := make(chan int)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Output channel, this instance&nbsp; &nbsp; fmt.Printf("%v ", mine)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Print this instance's prime&nbsp; &nbsp; for next := <-inch; next > 0; next = <-inch { // Read input channel&nbsp; &nbsp; &nbsp; &nbsp; if (next % mine) > 0 {&nbsp; &nbsp; &nbsp; &nbsp; // Divisible by my prime?&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if start {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // No; first time through?&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go sieve(next, ouch, done, count+1) // First number,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // create instance for it&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; start = false&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// First time done&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Not first time&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ouch <- next&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Pass to next instance&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; if start {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Just starting?&nbsp; &nbsp; &nbsp; &nbsp; close(done)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Yes - we're last in pipe - signal done&nbsp; &nbsp; &nbsp; &nbsp; print("\n",count," primes\n") // Number of primes/goroutines&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; close(ouch)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// No - send the signal down the pipe&nbsp; &nbsp; }}func main() {&nbsp; &nbsp; lim := 100&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Let's do up to 100&nbsp; &nbsp; done := make(chan int)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Create the done return channel&nbsp; &nbsp; ouch := make(chan int)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Create the first segment of the pipe&nbsp; &nbsp; go sieve(2, ouch, done, 1)&nbsp; &nbsp; &nbsp; &nbsp; // Create the first instance for '2'&nbsp; &nbsp; for prime := 3; prime < lim; prime += 1 { // Generate odd numbers&nbsp; &nbsp; &nbsp; &nbsp; ouch <- prime&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Send numbers down the pipe&nbsp; &nbsp; }&nbsp; &nbsp; close(ouch)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// Send the done signal down the pipe&nbsp; &nbsp; <- done&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// and wait for it to come back}与许多其他语言相比,Go 对于这种编程的优雅和简单给我留下了深刻的印象。当然,疣是我自己要求的。如果在这里合适,我欢迎批评性评论。

狐的传说

至于您的标题问题,当您不再需要它们时杀死工作程序 goroutine: 您可以使用 Done 成语。从封闭通道读取产生零值。创建一个新频道done。当从这个通道读取成功时,goroutines 知道他们应该退出。当您拥有所需的所有值时,关闭主通道。检查您是否可以从 channel 读取done,并通过返回退出,或者在可用时从 next 读取。这部分替换了nextfor 循环中的赋值:select {case <-done:returncase next = <- inch:}在通道上测距也有效,因为关闭该通道退出循环。至于反过来,你的身体问题,等待一组goroutines完成:使用sync.WaitGroup.var wg sync.WaitGroupwg.Add(goroutineCount)当每个 goroutine 完成时:wg.Done()或者使用延迟:defer wg.Done()等待所有这些都报告为完成:wg.Wait()在您的示例中,只需wg.Add(1)在启动新 goroutine 时调用,然后再调用wg.Done()和返回即可。只要您只达到零一次,就会wg.Wait()按预期工作,所以wg.Add(1)在wg.Done.
随时随地看视频慕课网APP

相关分类

Go
我要回答