Je dois utiliser defer
pour libérer les allocations créées manuellement à l'aide de la bibliothèque C
, mais je dois également os.Exit
avec un état non 0 à un moment donné. La partie délicate est que os.Exit
ignore toute instruction différée:
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)
}
Aire de jeux: http://play.golang.org/p/CDiAh9SXRM volé à https://gobyexample.com/exit
Alors, comment quitter un programme go honorant les appels defer
déclarés? Existe-t-il une alternative à os.Exit
?
runtime.Goexit()
est le moyen facile d'accomplir cela.
Goexit termine le goroutine qui l'appelle. Aucun autre goroutine n'est affecté. Goexit exécute tous les appels différés avant de terminer le goroutine. Parce que Goexit n'est pas une panique, cependant, tout appel de récupération dans ces fonctions différées retournera zéro.
Pourtant:
L'appel de Goexit à partir du goroutine principal met fin à ce goroutine sans retour de la fonction principale. Puisque func main n'est pas revenu, le programme continue l'exécution d'autres goroutines. Si tous les autres goroutines sortent, le programme plante.
Donc, si vous l'appelez depuis le goroutine principal, en haut de main
vous devez ajouter
defer os.Exit(0)
En dessous, vous voudrez peut-être ajouter d'autres instructions defer
qui informent les autres goroutines de s'arrêter et de nettoyer.
Faites simplement descendre votre programme d'un niveau et retournez votre code de sortie:
package main
import "fmt"
import "os"
func doTheStuff() int {
defer fmt.Println("!")
return 3
}
func main() {
os.Exit(doTheStuff())
}
Après quelques recherches, référez-vous à ceci this , j'ai trouvé une alternative qui:
Nous pouvons profiter de panic
et recover
. Il s'avère que panic
, par nature, honorera les appels defer
mais se terminera toujours avec non 0
code d'état et vidage d'une trace de pile. L'astuce est que nous pouvons remplacer le dernier aspect du comportement de panique avec:
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
}
}
Maintenant, pour quitter un programme à tout moment et conserver toute instruction defer
déclarée, il nous suffit d'émettre un type Exit
:
func main() {
defer handleExit() // plug the exit handler
defer fmt.Println("cleaning...")
panic(Exit{3}) // 3 is the exit code
}
Il ne nécessite aucune refactorisation à part le fait de brancher une ligne à l'intérieur de func main
:
func main() {
defer handleExit()
// ready to go
}
Cela évolue assez bien avec des bases de code plus grandes, je vais donc le laisser disponible pour examen. J'espère que cela aide.
Aire de jeux: http://play.golang.org/p/4tyWwhcX0-
Pour la postérité, c'était pour moi une solution plus élégante:
func main() {
retcode := 0
defer func() { os.Exit(retcode) }()
defer defer1()
defer defer2()
[...]
if err != nil {
retcode = 1
return
}
}