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
に代わるものはありますか?
runtime.Goexit()
はそれを達成する簡単な方法です。
Goexitは、呼び出し元のゴルーチンを終了します。他のゴルーチンは影響を受けません。 Goexitは、ゴルーチンを終了する前にすべての遅延呼び出しを実行します。Goexitはパニックではないため、これらの遅延関数の回復呼び出しはすべてnilを返します。
しかしながら:
メインゴルーチンからGoexitを呼び出すと、func mainが戻ることなく、そのゴルーチンが終了します。 func mainが返されないため、プログラムは他のゴルーチンの実行を継続します。他のすべてのゴルーチンが終了すると、プログラムはクラッシュします。
メインゴルーチンから呼び出す場合、main
の先頭に追加する必要があります
defer os.Exit(0)
その下に、他のゴルーチンに停止してクリーンアップするよう通知する他のdefer
ステートメントを追加することができます。
プログラムを1レベル下に移動して、終了コードを返すだけです。
package main
import "fmt"
import "os"
func doTheStuff() int {
defer fmt.Println("!")
return 3
}
func main() {
os.Exit(doTheStuff())
}
いくつかの調査の後、これを参照してください this 、私は次の代替案を見つけました:
panic
とrecover
を利用できます。 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
}
これは、より大きなコードベースで非常にうまくスケーリングするので、精査のために利用可能にしておきます。それが役に立てば幸い。
後世のために、これはよりエレガントなソリューションでした:
func main() {
retcode := 0
defer func() { os.Exit(retcode) }()
defer defer1()
defer defer2()
[...]
if err != nil {
retcode = 1
return
}
}