次のコラムでは、golang チュートリアル コラムから Golang Recover の小さな落とし穴を紹介します。
1.error
Golang には強力で便利な例外がないため、多くの批判を受けてきました。 Java、PHP、Python などのほとんどの高級プログラミング言語には、try catch メカニズムがあり、この例外キャッチ メカニズムは、プログラムの動作中に発生する可能性のあるさまざまな予期せぬ状況に非常に便利に対処できます。
厳密に言えば、Go では、エラーと例外は 2 つの異なるタイプです。エラーは通常、プログラムによって生成される論理エラー、または予期せぬ予期せぬ状況を指し、例外は通常、コーナーなどのパニックを指します。 、セグメンテーション違反。
エラーについては、Golang は非常に原始的な方法を採用しています。発生する可能性のあるすべてのエラーを手動で処理し、通常は呼び出し元にエラーを返す必要があります。Go では次の記述方法が非常に一般的です:
package mainimport ( "errors" "fmt")func main() { s, err := say() if err != nil { fmt.Printf("%s\n", err.Error()) } else { fmt.Printf("%s\n", s) }}func say() (string, error) { // do something return "", errors.New("something error")}复制代码
この書き方の最大の問題点は、エラーごとに判定して処理する必要があり、非常に面倒なことですが、try catchの仕組みを使えば、複数の関数呼び出し時に発生する可能性のあるエラーを一律に処理でき、手間が省けます。コードも時間もほとんどありません。しかし、今日は Go の例外とエラー処理メカニズムについて説明するためにここにいるのではなく、ここでは簡単に説明するだけに留めておきます。
2.panic
通常、エラーはプログラムによって明示的に表示され返されますが、例外は暗黙的で予測不可能であることがよくあります。次のコード:package mainimport "fmt"func main() { fmt.Printf("%d\n", cal(1,2)) fmt.Printf("%d\n", cal(5,2)) fmt.Printf("%d\n", cal(5,0)) //panic: runtime error: integer pide by zero fmt.Printf("%d\n", cal(9,5))}func cal(a, b int) int { return a / b}复制代码3 回目の計算を実行するとパニックが発生します。このエラーによりプログラムが終了し、次のコードは実行できません。もちろん、この種のエラーは理論的には予測可能であり、cal 関数内で対処する必要があるだけであると言えます。 しかし、実際の開発ではパニックが発生する箇所は多く、一目でわかるものではなく、Web サービスの場合、このようなパニックが発生すると Web サービス全体がハングしてしまいます。特に危険です。
3.recover
try catch メカニズムはありませんが、Go には実際に同様の回復メカニズムがあります。使い方はとても簡単です。:
package mainimport "fmt"func main() { fmt.Printf("%d\n", cal(1, 2)) fmt.Printf("%d\n", cal(5, 2)) fmt.Printf("%d\n", cal(5, 0)) fmt.Printf("%d\n", cal(9, 2))}func cal(a, b int) int { defer func() { if err := recover(); err != nil { fmt.Printf("%s\n", err) } }() return a / b}复制代码
まず、皆さんは defer の役割を理解する必要があります。簡単に言うと、defer はオブジェクト指向におけるデストラクタに似ています。この関数が終了したときにも実行されます。パニックによって終了した場合。
つまり、cal 関数は終了するたびに例外が発生したかどうかをチェックし、例外が発生した場合にはログを記録するなどの処理を行うことで、プログラムの実行を継続できます。
4. 注意すべき落とし穴一般に、遅延回復メカニズムは、Web サービスなどの常駐プロセス アプリケーションでよく使用されます。内部では、各 Web リクエストには処理する goroutine が割り当てられます。何も処理しないと、特定のリクエストでパニックが発生すると、サービス全体がハングアップします。これは許容できないため、Web アプリケーションで使用する必要があります。 1 つのリクエストでエラーが発生した場合でも、他のリクエストが影響を受けないようにリカバリします。
ここでは、シミュレートするために小さなコードを使用します:
package mainimport ( "fmt")func main() { requests := []int{12, 2, 3, 41, 5, 6, 1, 12, 3, 4, 2, 31} for n := range requests { go run(n) //开启多个协程 } for { select {} }}func run(num int) { //模拟请求错误 if num%5 == 0 { panic("请求出错") } fmt.Printf("%d\n", num)}复制代码
コルーチンの 1 つが必然的にパニックを起こし、アプリケーション全体がハングするため、上記のコードは完全には実行できません。も実行を停止します。
解決策は上記と同じで、 run 関数に defer Recovery を追加するだけで、プログラム全体が非常に堅牢になり、たとえパニックが発生しても完全に実行されます。
func run(num int) { defer func() { if err := recover();err != nil { fmt.Printf("%s\n", err) } }() if num%5 == 0 { panic("请求出错") } fmt.Printf("%d\n", num)}复制代码
上記のコードは単なるデモンストレーションです。本当の落とし穴は、run 関数で他のコルーチンを開始すると、このコルーチンで発生したパニックを回復できず、プロセス全体が引き続き停止することです。ハングします。上記の例を変更しました:
func run(num int) { defer func() { if err := recover(); err != nil { fmt.Printf("%s\n", err) } }() if num%5 == 0 { panic("请求出错") } go myPrint(num)}func myPrint(num int) { if num%4 == 0 { panic("请求又出错了") } fmt.Printf("%d\n", num)}复制代码
run 関数のコルーチンを通じて別の関数を呼び出しましたが、この関数もパニックになります。プログラム全体もハングしていることがわかります。run 関数が実行されていても、回復しても効果はありません。つまり、myPrint 関数に回復を追加する必要があります。ただし、コルーチンを使用して myPrint 関数を呼び出さない場合でも、直接呼び出してリカバリをキャプチャできます。
###要約すると、遅延回復メカニズムは、現在の関数と直接呼び出された関数によって生成される可能性のあるパニックのみを対象としています。その呼び出しによって生成された他のコルーチンのパニックは処理できません。これは、キャッチ機構を試してみます。 ###理論的には、アプリケーションが絶対確実であることを保証するために、コルーチンが使用されるすべての場所は遅延リカバリする必要があります。ただし、開発中の実際の状況に応じて決定できます。問題が発生する可能性が低いいくつかの機能を追加すると、パフォーマンスにも影響します。
Go の Web サービスについても同様です。デフォルトの回復メカニズムでは 1 つのレイヤーしかキャプチャできません。このリクエストの処理に他のコルーチンを使用する場合は、細心の注意を払う必要があります。結局のところ、パニックが発生する限り、 Web サービス全体がハングします。
最後に、要約すると、Go の例外処理メカニズムは他の多くの言語ほど効率的ではありませんが、基本的にはニーズを満たすことができます。公式は現在これを改善しており、Go2 もそれを目にするかもしれません。
推奨: 「go 言語チュートリアル」
以上がGolang Recover の小さな落とし穴を記録するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。