go - quiere - como cerrar un programa que se trabo
¿Cómo salir de un programa go en honor a las llamadas diferidas? (4)
Necesito defer
las asignaciones gratuitas creadas manualmente utilizando la biblioteca C
, pero también necesito os.Exit
de os.Exit
con estado no 0 en algún momento. La parte os.Exit
es que os.Exit
omite cualquier instrucción diferida:
package main
import "fmt"
import "os"
func main() {
// `defer`s will _not_ be run when using `os.Exit`, so
// this `fmt.Println` will never be called.
defer fmt.Println("!")
// sometimes ones might use defer to do critical operations
// like close a database, remove a lock or free memory
// Exit with status code.
os.Exit(3)
}
Área de juegos: http://play.golang.org/p/CDiAh9SXRM robado de https://gobyexample.com/exit
Entonces, ¿cómo salir de un programa go en honor a las llamadas declaradas defer
? ¿Hay alguna alternativa a os.Exit
?
Después de algunas investigaciones, refiérase a this , encontré una alternativa que:
- No impone una cierta arquitectura como en https://.com/a/27629493/438563
- No requiere ningún valor global como en https://.com/a/24601700/438563
Podemos aprovechar el panic
y recover
. Resulta que, por naturaleza, el panic
honrará las llamadas defer
, pero también siempre saldrá con un código de estado que no sea 0
y arrojará un rastro de pila. El truco es que podemos anular el último aspecto del comportamiento de pánico con:
package main
import "fmt"
import "os"
type Exit struct{ Code int }
// exit code handler
func handleExit() {
if e := recover(); e != nil {
if exit, ok := e.(Exit); ok == true {
os.Exit(exit.Code)
}
panic(e) // not an Exit, bubble up
}
}
Ahora, para salir de un programa en cualquier punto y aún conservar cualquier instrucción de defer
declarada, solo debemos emitir un tipo de Exit
:
func main() {
defer handleExit() // plug the exit handler
defer fmt.Println("cleaning...")
panic(Exit{3}) // 3 is the exit code
}
No requiere ninguna refactorización aparte de conectar una línea dentro de func main
:
func main() {
defer handleExit()
// ready to go
}
Esto se escala bastante bien con bases de códigos más grandes, así que lo dejaré disponible para el escrutinio. Espero eso ayude.
Área de juegos: http://play.golang.org/p/4tyWwhcX0-
Para la posteridad, para mí esta era una solución más elegante:
func main() {
retcode := 0
defer func() { os.Exit(retcode) }()
defer defer1()
defer defer2()
[...]
if err != nil {
retcode = 1
return
}
}
Simplemente mueva su programa a un nivel y devuelva su código de salida:
package main
import "fmt"
import "os"
func doTheStuff() int {
defer fmt.Println("!")
return 3
}
func main() {
os.Exit(doTheStuff())
}
runtime.Goexit()
es la forma más fácil de lograr eso.
Goexit termina la rutina que lo llama. Ningún otro goroutine se ve afectado. Goexit ejecuta todas las llamadas diferidas antes de terminar la rutina. Debido a que Goexit no entra en pánico, sin embargo, cualquier llamada de recuperación en esas funciones diferidas devolverá cero.
Sin embargo:
Llamar a Goexit desde el goroutine principal termina ese goroutine sin func main return. Como func main no ha regresado, el programa continúa la ejecución de otros goroutines. Si todas las otras rutinas salen, el programa se bloquea.
Entonces, si lo llamas desde la goroutine principal, en la parte superior de la página main
debes agregar
defer os.Exit(0)
Debajo, puede agregar otras declaraciones de aplazamiento que informen a los otros goroutines que se detengan y limpien.