无限运行Go协程

6

我有一个调用另一个函数并返回 chan 数组的函数。我目前正在使用 for 循环遍历数组中的范围,这会无限期地运行程序,并输出通过通道更新的结果。

func getRoutes() {
      for r := range rtu {
        if r.Type == 24 {
          fmt.Printf("Route added: %s via %s\n",r.Dst.String(),r.Gw.String())
        } else if r.Type == 25 {
          fmt.Printf("Route deleted: %s via %s\n",r.Dst.String(),r.Gw.String())
        }
      }
}

当我在main()函数中调用getRoutes()函数时,一切都按计划进行,但它会阻塞应用程序。我尝试从main()函数中调用go getRoutes(),但似乎根本没有调用该函数。
如何使用Go协程以非阻塞的方式在后台运行此函数?

这是我之前回答同一个问题时的答案。https://dev59.com/ymkw5IYBdhLWcg3w8fBJ#38856572 - 它使用了 runtime 包中的一种方法。 - jmaloney
3个回答

12

当主 goroutine 在 main 退出时,你可能会产生的所有 goroutine 都将变为孤儿并最终死亡。

你可以使用 for {} 的无限循环使主 goroutine 永远运行,但你可能想要保留一个退出通道:

exit := make(chan string)

// Spawn all you worker goroutines, and send a message to exit when you're done.

for {
    select {
    case <-exit:
        os.Exit(0)
    }
}

更新: Cerise Limón 指出,当主程序退出时,goroutine会立即被终止。我认为这应该是官方指定的行为。

另一个更新:当您有多种退出方式/多个退出通道时,使用 for-select 会更有效。对于单个退出通道,可以直接执行以下操作:

<-exit

最后使用forselect循环代替。


select 语句中出现语法错误:语法错误:意外的分号或换行符,期望逗号或 } - Alex Turner
1
更准确地说,当main函数返回时,程序退出。 Goroutine不会在孤立状态下继续运行并最终死亡。当程序退出时,进程中的所有执行都停止。 - Charlie Tumahai
这也是我的想法,但我看到一些奇怪的行为 - goroutines在主函数退出很久后仍然记录到STDOUT。也许 go test --race 只是将程序放入后台运行,或者在一个测试框架中运行主函数。 - Sudhir Jonathan
1
此外,您关于使用 for {} 的建议也是不正确的,因为繁忙循环始终是编程错误,它浪费了一个 CPU 的 100%,并最终导致运行时调度程序挂起。 - JimB
只是补充一下,你可以使用sync.WaitGroup代替for {}。 - Amir Keibi
显示剩余3条评论

5
尽管其他人已经回答了这个问题,但我认为他们的解决方案在某些情况下可以简化。
请看这段代码片段:
func main() {
    go doSomething()
    fmt.Println("Done.")
}

func doSomething() {
     for i := 0; i < 10; i++ {
        fmt.Println("Doing something...")
        time.Sleep(time.Second)
     }
}

main()开始执行时,它会启动一个线程同时执行doSomething()。然后立即执行fmt.Println("Done."),并且main()执行完毕。

main()完成时,所有其他goroutine都将变为孤儿,并死亡。
为了避免这种情况,我们可以在main()的结尾放置一个阻塞操作,等待来自goroutine的输入。最简单的方法是使用通道:
var exit = make(chan bool)

func main() {
    go doSomething()
    <-exit // This blocks until the exit channel receives some input
    fmt.Println("Done.")
}

func doSomething() {
     for i := 0; i < 10; i++ {
        fmt.Println("Doing something...")
        time.Sleep(time.Second)
     }
     exit<-true // Notify main() that this goroutine has finished
}

1

main()函数在getRoutes()协程完成之前返回。当main()函数返回时,程序退出,从而终止所有正在运行的协程。(也完全有可能,main()函数甚至在协程有机会被Go运行时调度之前就返回了)

如果你希望main()(或任何其他函数)等待一组协程完成,你需要显式地让该函数等待。有多种方法可以做到这一点。sync.WaitGroup.Wait()可以用于等待一组协程完成。你也可以使用通道来通信以确定协程何时完成。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接