如何退出执行延迟调用的 Go 程序?

问题描述 投票:0回答:4

我需要使用

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/CDiAh9SXRMhttps://gobyexample.com/exit

盗取

那么如何退出执行声明的

defer
调用的 Go 程序呢?除了
os.Exit
还有其他选择吗?

go exit defer-keyword
4个回答
60
投票

只需将程序下移一个级别并返回退出代码:

package main

import "fmt"
import "os"

func doTheStuff() int {
    defer fmt.Println("!")

    return 3
}

func main() {
    os.Exit(doTheStuff())
}

40
投票

runtime.Goexit()
是实现这一目标的简单方法。

Goexit 终止调用它的 goroutine。没有其他 goroutine 受到影响。 Goexit 在终止 goroutine 之前运行所有延迟调用。 然而,因为 Goexit 不会恐慌,所以这些延迟函数中的任何恢复调用都将返回 nil。

但是:

从主 Goroutine 调用 Goexit 会终止该 Goroutine,而不会返回 func main。由于 func main 还没有返回,程序继续执行其他 goroutine。如果所有其他 goroutine 都退出,程序就会崩溃。

因此,如果您从主 Goroutine 调用它,则需要在

main
的顶部添加

defer os.Exit(0)

在下面,您可能需要添加一些其他

defer
语句,通知其他 goroutine 停止并清理。


34
投票

经过一番研究,参考这个这个,我找到了一个替代方案:

我们可以利用

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-


31
投票

对于后代,对我来说,这是一个更优雅的解决方案:

func main() { 
    retcode := 0
    defer func() {
        // see rubens21's comment and adapt
        os.Exit(retcode)
    }()
    defer defer1()
    defer defer2()

    [...]

    if err != nil {
        retcode = 1
        return
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.