web-dev-qa-db-ja.com

遅延呼び出しを尊重するgoプログラムを終了する方法は?

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

プレイグラウンド: http://play.golang.org/p/CDiAh9SXRM 盗まれた https://gobyexample.com/exit

では、宣言されたdefer呼び出しを尊重するgoプログラムを終了する方法は? os.Exitに代わるものはありますか?

50
marcio

runtime.Goexit() はそれを達成する簡単な方法です。

Goexitは、呼び出し元のゴルーチンを終了します。他のゴルーチンは影響を受けません。 Goexitは、ゴルーチンを終了する前にすべての遅延呼び出しを実行します。Goexitはパニックではないため、これらの遅延関数の回復呼び出しはすべてnilを返します。

しかしながら:

メインゴルーチンからGoexitを呼び出すと、func mainが戻ることなく、そのゴルーチンが終了します。 func mainが返されないため、プログラムは他のゴルーチンの実行を継続します。他のすべてのゴルーチンが終了すると、プログラムはクラッシュします。

メインゴルーチンから呼び出す場合、mainの先頭に追加する必要があります

defer os.Exit(0)

その下に、他のゴルーチンに停止してクリーンアップするよう通知する他のdeferステートメントを追加することができます。

19
EMBLEM

プログラムを1レベル下に移動して、終了コードを返すだけです。

package main

import "fmt"
import "os"

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

    return 3
}

func main() {
    os.Exit(doTheStuff())
}
30
Rob Napier

いくつかの調査の後、これを参照してください this 、私は次の代替案を見つけました:

panicrecoverを利用できます。 panicは本来defer呼び出しを尊重しますが、0以外のステータスコードで常に終了し、スタックトレースをダンプします。秘Theは、パニック動作の最後の側面を次のようにオーバーライドできることです。

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-

22
marcio

後世のために、これはよりエレガントなソリューションでした:

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

    [...]

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