运行一个无限循环的Go协程

huangapple go评论89阅读模式
英文:

Running a Go routine indefinitely

问题

我有一个调用另一个函数并返回一个通道数组的函数。我目前使用一个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协程?

英文:

I have a function that calls another function that returns a chan array. I currently have a for loop looping over the range in the array which runs the program indefinitely outputting the channel updates as the come through.

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

When I call getRoutes() from main() everything works as planned though, it's blocking the application. I've tried calling go getRoutes() from main() though it appears as if the function isn't being called at all.

How can I run this function in the background in a non-blocking way using go routines?

答案1

得分: 12

当主 goroutine 在 main 函数退出时,你可能创建的所有 goroutine 都会变成孤儿并最终死亡。

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

exit := make(chan string)

// 创建所有的工作 goroutine,并在完成后发送退出消息。

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

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

另一个更新: 当你有多种退出方式或多个退出通道时,使用 for-select 结构更加合适。对于单个退出通道,你可以在最后使用 <-exit,而不是使用 forselect 循环。

英文:

When the primary goroutine at main exits, all goroutines you might have spawned will be orphaned and eventually die.

You could keep the main goroutine running forever with an infinite loop with for {}, but you might want to keep an exit channel instead:

exit := make(chan string)

// Spawn all you worker goroutines, and send a message to exit when you&#39;re done.

for {
	select {
	case &lt;-exit:
		os.Exit(0)
	}
}

Update: Cerise Limón pointed out that goroutines are just killed immediately when main exits. I think this is supposed to be the officially specified behaviour.

Another update: The for-select works better when you have multiple ways to exit / multiple exit channels. For a single exit channel you could just do

&lt;-exit

at the end instead of a for and select loop.

答案2

得分: 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 // 这会阻塞,直到exit通道接收到输入
    fmt.Println("Done.")
}

func doSomething() {
     for i := 0; i < 10; i++ {
        fmt.Println("Doing something...")
        time.Sleep(time.Second)
     }
     exit <- true // 通知main()函数这个goroutine已经完成
}
英文:

Although other people have answered this, I think their solutions can be simplified in some cases.

Take this code snippet:

func main() {
    go doSomething()
    fmt.Println(&quot;Done.&quot;)
}

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

When main() begins executing, it spawns a thread to concurrently execute doSomething(). It then immediately executes fmt.Println(&quot;Done.&quot;), and the main() finishes.

When main() finishes, all other goroutines will be orphaned, and die.

To avoid this, we can put a blocking operation at the end of main() that waits for input from a goroutine. It's easiest to do this with a channel:

var exit = make(chan bool)

func main() {
    go doSomething()
    &lt;-exit // This blocks until the exit channel receives some input
    fmt.Println(&quot;Done.&quot;)
}

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

答案3

得分: 1

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

如果你想让main()(或任何其他函数)等待一组协程完成,你需要通过某种方式显式地让函数等待。有多种方法可以实现这一点。sync.WaitGroup.Wait()可以用来等待一组协程完成。你也可以使用通道来在协程完成时进行通信。

英文:

Your main() returns before getRoutes()-goroutine finishes. When main() returns, the program exits, thereby killing all the running goroutines. (It's also totally possible that main() returns even before the goroutine gets a chance to get scheduled by go runtime.)

If you want main() (or any other function) to wait for a group of goroutines to finish, you'd have to make the function explicitly wait somehow. There're multiple ways to do that. sync.WaitGroup.Wait() can be used to wait for a group of the goroutines to finish. You could also use channels to communicate when goroutines are done.

huangapple
  • 本文由 发表于 2017年1月4日 11:22:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/41455724.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定