Как выйти из программы, посвященной отсроченным вызовам?
Мне нужно использовать defer
для бесплатных распределений, созданных вручную с помощью библиотеки C
, но мне также нужно os.Exit
с не-статусом в какой-то момент. Сложная часть состоит в том, что os.Exit
пропускает любую отложенную инструкцию:
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)
}
Игровая площадка: http://play.golang.org/p/CDiAh9SXRM украден из https://gobyexample.com/exit
Итак, как выйти из программы go, выполняющей объявленные объявленные вызовы defer
? Есть ли альтернатива os.Exit
?
Ответы
Ответ 1
runtime.Goexit()
- это простой способ выполнить это.
Goexit завершает вызов goroutine, который вызывает его. Ни один другой горутин не пострадал. Goexit запускает все отложенные вызовы перед завершением goroutine. Поскольку Goexit не является паникой, однако любые вызовы восстановления в этих отложенных функциях возвращают нуль.
Однако:
Вызов Goexit из главного goroutine завершает работу goroutine без возврата func main. Поскольку func main не вернулся, программа продолжает выполнение других goroutines. Если все остальные goroutines выходят, программа выйдет из строя.
Итак, в верхней части main
вам просто нужно добавить
defer os.Exit(0)
Или, возможно, какая-то другая очистка, чтобы уведомить других горутов о том, что время для остановки, и вы хорошо.
Ответ 2
После некоторых исследований обратитесь к этому this, я нашел альтернативу, которая:
Мы можем использовать panic
и recover
. Оказывается, что panic
, по своей природе, будет чтить defer
вызовы, но также будет всегда выходить с кодом статуса 0
и выгружать трассировку стека. Фокус в том, что мы можем переопределить последний аспект панического поведения с помощью:
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
}
}
Теперь, чтобы выйти из программы в любой точке и сохранить любую объявленную инструкцию defer
, нам просто нужно исправить тип Exit
:
func main() {
defer handleExit() // plug the exit handler
defer fmt.Println("cleaning...")
panic(Exit{3}) // 3 is the exit code
}
Он не требует каких-либо рефакторинга, кроме подключения строки внутри func main
:
func main() {
defer handleExit()
// ready to go
}
Это хорошо масштабируется с большими кодовыми базами, поэтому я оставлю его доступным для проверки. Надеюсь, что это поможет.
Игровая площадка: http://play.golang.org/p/4tyWwhcX0-
Ответ 3
Просто переместите свою программу на уровень и верните код выхода:
package main
import "fmt"
import "os"
func doTheStuff() int {
defer fmt.Println("!")
return 3
}
func main() {
os.Exit(doTheStuff())
}
Ответ 4
Для потомков для меня это было более элегантное решение:
func main() {
retcode := 0
defer func() { os.Exit(retcode) }()
defer defer1()
defer defer2()
[...]
if err != nil {
retcode = 1
return
}
}