一般的に言えば、パニック ダウンタイムに入ったプログラムに対しては何もするべきではありませんが、場合によっては、ダウンタイムから回復する必要がある場合があります。たとえば、Web サーバーが予期せぬ重大な問題が発生した場合、クラッシュする前にすべての接続を閉じる必要があります。何もしなければ、クライアントは待機状態になります。Web サーバーがまだ開発段階にある場合、サーバーは例外情報を Web サーバーにフィードバックすることもできます。デバッグに役立つクライアント。
ヒント
他の言語では、ダウンタイムは例外の形で存在することがよくあります。最下層が例外をスローし、上位層のロジックが try/catch メカニズムを通じて例外をキャッチします。キャッチされない重大な例外はダウンタイムを引き起こします。キャッチされた例外は無視でき、コードは続きます。実行します。
Go 言語には例外システムがありません。パニックを使用してダウンタイムをトリガーすることは、他の言語で例外をスローするのと似ています。recover のダウンタイム回復メカニズムは、他の言語の try/catch メカニズムに対応します。
プログラムがクラッシュしても実行を継続できるようにする
次のコードは、ProtectRun() 関数を実装します。この関数は、クロージャの後に匿名関数または実行関数を渡します。受信関数が何らかの形式でパニックになると、クラッシュ エラーを出力し、後続のコードを許可することができます。コードは問題なく実行され続けます。プロセス全体がクラッシュします。
実行中の機能を保護します。
package main
import (
"fmt"
"runtime"
)
// パニック時に渡すコンテキスト情報
type panicContext struct {
function string // 関数名
}
// 保護された方法で関数を実行する
func ProtectRun(entry func()){
// 遅延処理関数
defer func(){
// パニックで渡されたコンテキストを取得して出力
error:= recover()
switch err.(type){
case runtime.Error:// 実行時エラー
fmt.Println("runtime error:", err)
default:// 実行時エラー以外
fmt.Println("error:", err)
}
}()
entry()
}
func main(){
fmt.Println("動作前")
// 手動エラーの実行を許可
ProtectRun(func() {
fmt.Println("手動パニックの前")
// パニックでコンテキストを渡す
panic(&panicContext {
"手動によるパニックの発生",
})
fmt.Println("手動パニックの後")
})
// ヌルポインターアクセスのエラーを意図的に引き起こす
ProtectRun(func() {
fmt.Println("割り当てパニックの前")
var a *int
*a = 1
fmt.Println("割り当てパニックの後")
})
fmt.Println("実行後")
}
コード出力結果:
動作前
手動パニックの前
手動によるパニックの発生
割り当てパニックの前
runtime error: runtime error: invalid memory address or nil pointer dereference
実行後
コードの説明:
- 9 行目では、エラーを説明する構造体を宣言し、エラーを実行する関数を保存します。
- 17 行目では、defer を使用してクロージャの実行を遅らせます。パニックがクラッシュを引き起こすと、ProtectRun() 関数は実行を終了し、defer の後のクロージャが呼び出されます。
- 20 行目で、recover() は、panic によって渡されたパラメーターを取得します。
- 22 行目で、switch を使用して、err 変数に対して型アサーションを実行します。
- 行 23 では、エラーが、ヌル ポインター アクセス、除数が 0 など、ランタイム層によってスローされたランタイム エラーである場合、ランタイム エラーを出力します。
- 行 25 のその他のエラーは、渡されたエラー データを出力します。
- 44 行目で、panic を使用して手動でエラーをトリガーし、情報を含む構造体を渡します。このとき、recover は構造体の情報を取得して出力します。
- 行 57、シミュレートされたコード内の null ポインター割り当てによって引き起こされたエラーは、ランタイム層によってスローされ、ProtectRun() 関数のcover() 関数によって捕捉されます。
パニックと回復の関係
パニックとリカバリの組み合わせには次の特徴があります。
- パニックが発生しますが回復はなく、プログラムがクラッシュします。
- パニックと回復が発生します。プログラムはダウンしません。対応する遅延を実行した後、ダウンタイムの時点から現在の関数を終了し、実行を続行します。
ヒント
パニック/リカバリは他の言語の例外メカニズムをシミュレートできますが、通常の関数を作成するときにこの機能を頻繁に使用することはお勧めできません。
パニックによってトリガーされる defer 関数では、プログラム全体がクラッシュするまでパニックを呼び出し続け、さらにエラーをスローできます。
エラーが捕捉されたときに現在の関数の戻り値を設定したい場合は、名前付き戻り値メソッドを使用して戻り値を直接設定できます。