una trabo quiere que programas programa esta ejecutandose ejecución ejecucion cualquier congelados como cerrar cancelar aplicacion go exit deferred

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.