ProgrammingシニアGo開発者

Goでは、組み込み関数recoverとpanicがどのように実装されているか説明してください。goroutine内での安全な回復のための正しい使用順序は何ですか?

Hintsage AIアシスタントで面接を突破

回答

Goでは、パニック(panic)はプログラム内の致命的なエラーを通知するために使用されます。パニックが発生した後、スタックのアンワインドが始まり、すべてのdeferred関数が呼び出されます。スタックにrecover()を呼び出す関数がある場合、それはパニックを捕まえて処理することができますが、recoverはそのゴルーチンの内部でのみ機能し、deferから呼び出されたときのみ機能します。

安全な回復のための推奨パターンは次のとおりです:

func safe(fn func()) { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() fn() } go safe(func() { panic("fail!") // プログラムの停止には至らない })

重要なことは、

  • panicは現在のゴルーチンの実行を最初のrecoverまで終了させる
  • Recoverは同じゴルーチン内のdeferからのみ機能する
  • Recoverが使用されない場合、パニックはプログラム全体を終了させる

問題のある質問

recoverは、他のゴルーチンで発生したパニックを捕まえることができますか?

回答: いいえ、recoverはパニックが発生したゴルーチン内でのみ機能し、defer関数から呼び出された場合のみ有効です。あるゴルーチンでパニックが発生し、別のゴルーチンでrecoverが呼び出されても、捕まえることはできず、プログラムは異常終了します。

例:

func main() { go func() { panic("inside goroutine") }() time.Sleep(time.Second) recover() // 機能しない! Panicはプログラムを終了させる }

このテーマに関する実際のエラーの例


ストーリー

開発者は、main関数内でのみdefer recover()パターンを使用してエラー処理を実装した。ワーカー内でパニックが発生したとき、プログラム全体が異常終了した — グローバルrecoverがエラーを捕まえなかった。


ストーリー

プロジェクトでは、ウェブサーバーの優雅なシャットダウンのためにdefer/recoverを使用したが、それで十分だと考えていた。一つのゴルーチンのパニックがプロセス全体を終了させることが判明した。recoverの処理が正しい位置に配置されていなかったため、ワーカープール全体のリファクタリングが必要だった。


ストーリー

メッセージ処理のスレッドで、ユーザ関数の不適切な動作によりパニックが発生した。開発者は、トップレベルのrecoverがそれを「捕まえる」ことを期待したが、recoverがdeferからのみ機能することを理解していなかった。その結果、サービスは不規則にクラッシュし、キューに不正なメッセージが現れると発生した。