Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何退出接受延迟呼叫的go计划?_Go_Exit_Deferred - Fatal编程技术网

如何退出接受延迟呼叫的go计划?

如何退出接受延迟呼叫的go计划?,go,exit,deferred,Go,Exit,Deferred,我需要使用defer来释放使用Clibrary手动创建的分配,但我还需要os。在某个时刻以非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 fm

我需要使用
defer
来释放使用
C
library手动创建的分配,但我还需要
os。在某个时刻以非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)
}
游乐场:从


那么,如何退出接受已声明的
defer
调用的go程序呢?除了操作系统退出,还有其他选择吗?

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

package main

import "fmt"
import "os"

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

    return 3
}

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

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

  • 不会像中那样强加特定的体系结构
  • 不需要像中那样的任何全局值
我们可以利用
恐慌
恢复
。事实证明,本质上,
panic
会接受
延迟
调用,但也会始终以非
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
}
这在更大的代码基础上可以很好地扩展,所以我将把它留给仔细检查。希望能有帮助

操场:

是实现这一目标的简单方法

Goexit终止了称之为goroutine的goroutine。没有其他goroutine受到影响Goexit在终止goroutine之前运行所有延迟调用。因为Goexit不是死机,但是,这些延迟函数中的任何recover调用都将返回nil

然而:

从主goroutine调用Goexit将终止该goroutine,而不返回func main。由于func main尚未返回,程序将继续执行其他goroutine。如果所有其他goroutine退出,程序将崩溃

因此,如果您从主goroutine调用它,则需要在
main
的顶部添加

defer os.Exit(0)

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

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

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

    [...]

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

@ctcherry延迟
操作系统没有成功。退出
:反转延迟顺序哦,我明白了,这是个问题。在我的情况下,我只能延迟
os。在运行其他也延迟某些内容的操作后退出
。。。让我再想想。
main
中的
defer
os结合使用。退出
肯定是个棘手的问题。像下面的@Rob Napier那样围绕它进行架构是一种更好的方法。因此,我不应该在
func main
内部使用
defer
或任何退出的函数?更重要的是,我不建议在代码中的随机位置使用
os.Exit()
。除了错误代码的问题外,这使得测试非常困难。peterSO的解决方案@ctcherry links是可以的,但是它不能很好地扩展到更大的程序中。您必须将
代码
设置为全局。我相信你应该保持main()相当简单,让它只处理操作系统级别的事情(比如最终状态代码)。无论如何,+1,因为总是先尝试亲吻是个好建议。我不知道
runtime.Goexit()
的存在。这是最近发布的吗?@marcio我在Go存储库中做了一些挖掘。我找不到它的确切介绍时间,但我确实在你发布这个问题之前找到了。@marcio我做了更多的挖掘并找到了。Goexit列在那里。我将把它标记为可接受的答案。其他答案肯定仍然有效,但这似乎是最简单的方法——就目前而言:迄今为止最好的答案!一个问题,如何处理正常退出呢?如何确保在正常退出的情况下调用
handleExit
?Ref:un comment the
//panic
并查看其区别。我认为正常退出0事件的句柄不存在,但您可以总是先进行panic(退出{0}),然后再进行处理,或者在main()上执行任何其他操作之前延迟handleNormalExit()?这确实占用了其他答案中最好的部分。