順序に依存する複数のdefer
ステートメントを発行したり、ロジックをパッケージ化する匿名関数を延期したりする方が安全または慣用的ですか?
例:
defer os.Remove(tempFile.Name())
defer tempFile.Close()
上記の場合、構文は最小限ですが、延期の順序は実行されるロジックと逆です。
以下の場合、より多くの行、より多くの「構文」がありますが、ロジックはより自然な順序になっています。
defer func() {
tempFile.Close()
os.Remove(tempFile.Name())
}()
どちらを使用しますか?
この例では、特にエラー処理を追加すると、無名関数が読みやすくなります。
f, err := ioutil.TempFile("", "prefix")
if err != nil {
log.Println("creating temp file:", err)
return
}
defer func() {
err := f.Close()
if err != nil {
log.Println("close:", err)
}
err = os.Remove(f.Name())
if err != nil {
log.Println("remove:", err)
}
}()
複数のリソースがある場合、通常は複数のdefer
sが適切です。
複数のリソースがある場合は、通常、複数のdeferが適切です。
2019年4月:ただし、その場合は、Go 1.13(2019年第4四半期)を検討してください。これは、 go issue 14939: "runtime:defer is slow" および go issue 6980: "cmd/compile:一部の遅延をスタックフレームに割り当てます"
参照 Go CL 171758: "cmd/compile、runtime:allocate defer records on the stack"
関数本体で延期が最大で1回実行されると、その延期レコードをヒープではなくスタックに割り当てることができます。
これにより、このような遅延(非常に一般的)が速くなります。
この最適化は、cmd/goバイナリの370の静的遅延サイトのうち363に適用されます。
name old time/op new time/op delta Defer-4 52.2ns ± 5% 36.2ns ± 3% -30.70% (p=0.000 n=10+10)
2019年10月(Go 1.13は数週間前にリリースされました)
これは 確認済み(Brad Fitzpatrick) with CL 190098 :
据え置きステートメントのコスト[
go test -run NONE -bench BenchmarkDefer$ runtime
]With normal (stack-allocated) defers only: 35.4 ns/op With open-coded defers: 5.6 ns/op Cost of function call alone (remove defer keyword): 4.4 ns/op
しかし Damien Griskyが追加 :
Deferの方が安くなりますが、パニック/回復はより高価です。
Cost of defer: 34ns -> 6ns.
Cost of panic/recover: 62ns -> 255ns
それは悪いトレードオフではありません。