Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang: Why os.Exit doesn't work inside goroutines

Tags:

go

goroutine

I have a research program with very simple algorithm. When success is coming goroutine should be close (end) via os.Exit(0). I'm wait one day, two day.... What? :)

Here is the simple code

package main

import "os"

func main() {
    for {
        go func() { os.Exit(0) }()
    }
}

And my questions:

  1. Why os.Exit doesn't terminate the goroutine?
  2. What is correct way to terminate (stop) goroutine execute?

Playground: http://play.golang.org/p/GAeOI-1Ksc

like image 882
A L Avatar asked Jan 19 '16 18:01

A L


People also ask

What happens to Goroutines when main exits?

On exit (once main goes away, or if os. Exit is called) the goroutines are killed off (no deferred functions are run). Note - to get goroutines to quit a 'broadcast' quit channel can be created - as a closed channel starts to return which means a global quit channel can be included in all selects in goroutines and …

How do you exit a program in golang?

To exit an application in Go, use the os. Exit() function from the os package. It causes the program to terminate immediately. The function takes a status code as an argument where the code zero indicates success and the non-zero an error.

Are Goroutines blocking?

When we send data into the channel using a GoRoutine, it will be blocked until the data is consumed by another GoRoutine. When we receive data from channel using a GoRoutine, it will be blocked until the data is available in the channel.

How do I get out of Goroutine gracefully?

Typically, you pass the goroutine a (possibly separate) signal channel. That signal channel is used to push a value into when you want the goroutine to stop. The goroutine polls that channel regularly. As soon as it detects a signal, it quits.


1 Answers

You've run into a sticky corner of the Go scheduler. The answer is that os.Exit does cause the entire process to exit, but the way you had it, the goroutines were never running.

What probably happened was that the for loop kept adding new goroutines to the list of available goroutines, but since the entire process was only running in one OS thread, the Go scheduler never got around to actually scheduling a different goroutine, and just kept running that for loop without ever running any of the goroutines you'd spawned. Try this instead:

package main

import "os"

func main() {
    for {
        go func() { os.Exit(0) }()
        func() {}()
    }
}

If you run it on the Go Playground, it should work (in fact, here's a link).

OK, the fact that the above code works while yours doesn't should be pretty mysterious. The reason this works is that the Go scheduler is actually non-preempting. What that means is that unless a given goroutine voluntarily decides to give the scheduler the option to run something else, nothing else will run.

Now obviously you've never written code that includes commands to give the scheduler a chance to run. What happens is that when your code is compiled, the Go compiler automatically inserts these into your code. And here's the key to why the above code works: one of the times that a goroutine might decide to run the scheduler is when a function is called. So by adding the func() {}() call (which obviously does nothing), we've allowed the compiler to add in a call to the scheduler, giving this code the opportunity to schedule different goroutines. Thus, one of the spawned goroutines runs, calls os.Exit, and the process exits.

EDIT: The function call itself may not be sufficient in the event that the compiler inlines the call (or, in this case, removes it entirely since it does nothing). runtime.Gosched(), on the other hand, is guaranteed to work.

like image 193
joshlf Avatar answered Nov 05 '22 20:11

joshlf