ロスライトの回答が述べているように:
複数のリソースがある場合は、通常、複数の defer が適切です。
2019 年 4 月: ただし、その場合は、Go 1.13 (2019 年第 4 四半期) を検討してください。go issue 14939: "runtime: defer is slow"およびgo issue 6980: "cmd/compile: allocate some defer in stack frames "
Go CL 171758を参照してください: "cmd/compile,runtime: スタックに defer レコードを割り当てる"
関数本体で defer が最大 1 回実行される場合、その defer レコードをヒープではなくスタックに割り当てることができます。
これにより、このような遅延 (非常に一般的) が高速になります。
この最適化は、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 がリリースされました)
これはCL 190098で確認されています (Brad Fitzpatrick) :
繰延明細書の費用 [ 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
しかし、ダミアン・グリスキーは次のように付け加えています。
Defer は安くなりますが、panic/recover はより高くつきます。
Cost of defer: 34ns -> 6ns.
Cost of panic/recover: 62ns -> 255ns
それは悪いトレードオフではありません。
言い換えれば、複数の defer を使用することは慣用的である可能性がありますが、Go 1.13+ ではもはや問題にならないパフォーマンス コストによって、その慣行は抑制されました。
( Paschalisのブログ投稿 " What is a defer? And how many can you run? " で説明されているように)
これにより、if defer (コード フローに関係なく関数呼び出しを実行する必要がある場所) が可能になります。
ただし、John Refior氏は、これdefer
は同期的であると述べています。
実際には defer は関数が終了する直前に実行されます。
また、同期的に発生するため、呼び出し元は defer が完了するまで待機します。
したがって、複数の defer を使用できるようになったとしても、それらが高速であることを確認するか、John が指摘するように:
幸いなことに、ゴルーチンを a でラップするのは簡単でdefer
、呼び出し元を遅らせることなく、必要なフロー制御とタイミングを提供します。
func Handler(w http.ResponseWriter, r *http.Request) {
log.Println("Entered Handler")
defer func() {
go func() {
time.Sleep(5 * time.Second)
log.Println("Exiting goroutine")
}()
log.Println("Exiting defer")
}()
}
多くの場合、遅延はミューテックスのロック、接続またはファイル記述子のクローズに使用され、それらが行う作業は高速であるか、呼び出し元が移動する前に完了したい.
ただし、クライアントが HTTP ハンドラーの最後で待機する必要のない遅い作業を行っている場合は、呼び出しを非同期にすることで、ユーザー エクスペリエンスを大幅に向上させることができます。