Reputation: 510
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?
Upvotes: 6
Views: 17478
Reputation: 17516
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're done.
for {
select {
case <-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
<-exit
at the end instead of a for
and select
loop.
Upvotes: 12
Reputation: 2288
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("Done.")
}
func doSomething() {
for i := 0; i < 10; i++ {
fmt.Println("Doing something...")
time.Sleep(time.Second)
}
}
When main()
begins executing, it spawns a thread to concurrently execute doSomething()
. It then immediately executes fmt.Println("Done.")
, 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()
<-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
}
Upvotes: 5
Reputation: 360
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.
Upvotes: 1