我需要使用defer
释放使用C
库手动创建的分配,但我还需要os.Exit
在某个时刻处于非0状态。棘手的部分是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
那么如何退出一个尊重已宣布的defer
来电的go计划呢?除os.Exit
之外还有其他选择吗?
答案 0 :(得分:30)
只需将程序向下移动一级并返回退出代码:
package main
import "fmt"
import "os"
func doTheStuff() int {
defer fmt.Println("!")
return 3
}
func main() {
os.Exit(doTheStuff())
}
答案 1 :(得分:22)
经过一些研究,请参阅此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
}
这可以很好地扩展到更大的代码库,因此我将其留待审查。希望它有所帮助。
答案 2 :(得分:18)
runtime.Goexit()
是实现这一目标的简便方法。
Goexit终止调用它的goroutine。没有其他goroutine受到影响。 Goexit在终止goroutine之前运行所有延迟调用。因为Goexit不会出现恐慌,但是这些延迟函数中的任何恢复调用都将返回nil。
然而:
从主goroutine调用Goexit会终止那个没有func main返回的goroutine。由于func main尚未返回,程序继续执行其他goroutine。如果所有其他goroutine退出,程序崩溃。
所以如果你从主goroutine调用它,在main
的顶部你需要添加
defer os.Exit(0)
在下面你可能想要添加一些其他defer
语句,告知其他goroutines停止和清理。
答案 3 :(得分:8)
对于后人来说,对我而言,这是一个更优雅的解决方案:
func main() {
retcode := 0
defer func() { os.Exit(retcode) }()
defer defer1()
defer defer2()
[...]
if err != nil {
retcode = 1
return
}
}
答案 4 :(得分:0)
func main() {
var exitCode = 1
defer func() {
os.Exit(exitCode)
}()
defer f2()
defer f1()
err := myFunc()
if err != nil {
// executes all previous deferreds in order f1(), f2(), os.Exit(1)
return
}
err = myFunc2()
if err != nil {
// executes all previous deferreds in order f1(), f2(), os.Exit(1)
return
}
exitCode = 0
// All went well,
// executes all previous deferreds in order f1(), f2(), os.Exit(0)
}
答案 5 :(得分:-1)
我建议使用goroutine
func main(){
defer fmt.Println("Hello world")
go func(){
os.Exit(0)
}
}