如何退出一个尊重延期通话的go计划?

时间:2014-12-23 23:14:53

标签: go exit deferred

我需要使用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之外还有其他选择吗?

6 个答案:

答案 0 :(得分:30)

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

package main

import "fmt"
import "os"

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

    return 3
}

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

答案 1 :(得分:22)

经过一些研究,请参阅此this,我找到了另一种选择:

我们可以利用panicrecover。事实证明,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-

答案 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)
     }
}