Go语言 如何退出一个支持延期叫牌的围棋程序?

tpxzln5u  于 12个月前  发布在  Go
关注(0)|答案(4)|浏览(60)

我需要使用defer来释放使用C库手动创建的分配,但我还需要在某个时候使用非0状态的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)
}

Playground:http://play.golang.org/p/CDiAh9SXRMhttps://gobyexample.com/exit被盗
那么如何退出一个支持已声明的defer调用的go程序呢?有没有os.Exit的替代品?

toe95027

toe950271#

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

package main

import "fmt"
import "os"

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

    return 3
}

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

ovfsdjhp2#

runtime.Goexit()是实现这一点的简单方法。
Goexit终止调用它的goroutine。其他goroutine不受影响。**Goexit在终止goroutine之前运行所有的延迟调用。**因为Goexit不是panic,但是,这些延迟函数中的任何recover调用都将返回nil。
然而:
从主goroutine调用Goexit会终止该goroutine而不返回func main。由于func main没有返回,程序继续执行其他goroutine。如果所有其他goroutine退出,程序就会崩溃。
因此,如果你从主goroutine调用它,在main的顶部,你需要添加

defer os.Exit(0)

在下面,你可能想添加一些其他的defer语句,通知其他的goroutine停止并清理。

62lalag4

62lalag43#

经过一些研究,参考这个this,我发现了一个替代方案:

我们可以利用panicrecover。事实证明,panic本质上会荣誉defer调用,但也总是以非0状态码退出并转储堆栈跟踪。诀窍是我们可以使用以下命令覆盖panic行为的最后一个方面:

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
}

这在较大的代码库中扩展得很好,所以我将它留给仔细检查。希望有帮助。
Playground:http://play.golang.org/p/4tyWwhcX0-

oiopk7p5

oiopk7p54#

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

func main() { 
    retcode := 0
    defer func() { os.Exit(retcode) }()
    defer defer1()
    defer defer2()

    [...]

    if err != nil {
        retcode = 1
        return
    }
}

相关问题