在不知道可能引发 panic 的函数的情况下,继续执行恢复 panic 后的代码。

huangapple go评论80阅读模式
英文:

Continue execution after recovered panic without knowing which function might panic

问题

我被引用到这个问题:https://stackoverflow.com/questions/63867365/program-recovered-from-panic-does-not-exit-as-expected
它的工作正常,但它依赖于知道 panic 发生的位置以便放置延迟函数。
我的代码如下。

package main

import "fmt"

func main() {
	defer recoverPanic()
	f1()
	f2()
	f3()
}

func f1() {
	fmt.Println("f1")
}

func f2() {
	defer f3() //<--- 不想在这里延迟 f3,因为我可能不知道 f2 会 panic,panic 可能发生在其他地方
	fmt.Println("f2")
	panic("f2")
}

func f3() {
	fmt.Println("f3")
}

func recoverPanic() {
	if r := recover(); r != nil {
		fmt.Printf("Cause of panic =>> %q\n", r)
	}
}

在 panic 函数中延迟调用 f3() 是有效的,输出如下。

f1
f2
f3
Cause of panic =>> "f2"

如果你有一个应用程序,不知道 panic 发生在哪里,我需要在每个可能发生 panic 的函数中都放置一个延迟吗?
注释掉 defer f3() 后,我得到以下输出。

f1
f2
Cause of panic =>> "f2"

f3 永远不会运行。

我的问题是如何在没有在每个可能发生 panic 的函数中放置延迟函数调用的情况下继续执行程序?

英文:

I was referred to this question: https://stackoverflow.com/questions/63867365/program-recovered-from-panic-does-not-exit-as-expected
It works fine but it relies on knowing where the panic occurs in order to place the deferred function.
My code is as follows.

package main

import &quot;fmt&quot;

func main() {
	defer recoverPanic()
	f1()
	f2()
	f3()
}

func f1() {
	fmt.Println(&quot;f1&quot;)
}

func f2() {
	defer f3() //&lt;--- don&#39;t want to defer f3 here because I might not know f2 will panic, panic could occuer elsewhere
	fmt.Println(&quot;f2&quot;)
	panic(&quot;f2&quot;)
}

func f3() {
	fmt.Println(&quot;f3&quot;)
}

func recoverPanic() {
	if r := recover(); r != nil {
		fmt.Printf(&quot;Cause of panic ==&gt;&gt; %q\n&quot;, r)
	}
}

Having the deferred function call f3() in the panicking function works, output below.

f1
f2
f3
Cause of panic ==&gt;&gt; &quot;f2&quot;

What if you have an application where you don't know where a panic occurs, do I need to put a defer in every function that might panic?
Commenting out the defer f3() gives me the following output.

f1
f2
Cause of panic ==&gt;&gt; &quot;f2&quot;

f3 never runs.

My question is how to continue execution of the program without having a deferred function call in every function that might panic?

答案1

得分: 4

在发生 panic 后,无法恢复函数的执行。当当前的执行行无法继续正确执行时,会使用 panic。如果可以任意恢复 panic 后的执行(如果可能的话),那么就会立即引发另一个 panic,因为状态已经不正确,继续执行也无法修复。

例如,假设一个函数在尝试在切片上越界读取时发生 panic。它如何继续执行?继续执行意味着什么?它应该只是读取越界的内存位置并获取垃圾数据吗?继续执行时应该使用零值?从切片中取另一个值?

你必须处理错误情况,要么通过显式地恢复,要么通过预先检查/纠正可能导致 panic 的条件。至少在标准库中,可能引发 panic 的函数会在其文档中说明,并解释会导致 panic 的条件。

如果你经常需要安全地调用无返回值的函数并从任何 panic 中恢复,可以为此编写一个简单的包装函数。

func try(f func()) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("caught panic:", err)
		}
	}()
	f()
}

然后

func main() {
	try(f1)
	try(f2)
	try(f3)
}
英文:

You can't resume function execution after a panic. Panic is used when the current line of execution cannot continue correctly. Arbitrarily resuming execution after a panic (if it were possible) is begging immediately for another panic, because the state is already incorrect and just blazing ahead won't fix that.

For example, let's say a function panics when it tries to read out of bounds on a slice. How can it continue? What would it even mean to continue? Should it just read the out of bounds memory location and get garbage data? Continue with a zero value? Take a different value from the slice?

You must handle error cases; either by explicitly recovering, or preemptively checking / correcting conditions that will result in panic. At least in the standard library, functions that may spur a panic will say so in their documentation with an explanation of which conditions will result in panic.

If you commonly need to safely call void functions and recover from any panics, you can make a simple wrapper function for that.

func try(f func()) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println(&quot;caught panic:&quot;, err)
		}
	}()
	f()
}

Then

func main() {
	try(f1)
	try(f2)
	try(f3)
}

huangapple
  • 本文由 发表于 2022年6月27日 07:00:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/72765682.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定