We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Go 语言中两个经常成对出现的两个关键字 — panic 和 recover。这两个关键字与上一节提到的 defer 有紧密的联系,它们都是 Go 语言中的内置函数,也提供了互补的功能。
下面通过几个例子了解一下使用 panic 和 recover 关键字时遇到的这几个现象
panic 只会触发当前 Goroutine 的延迟函数调用,我们可以通过如下所示的代码了解该现象:
func main() { defer println("in main") go func() { defer println("in goroutine") panic("") }() time.Sleep(1 * time.Second) } $ go run main.go in goroutine panic: ...
当我们运行这段代码时会发现 main 函数中的 defer 语句并没有执行,执行的只有当前 Goroutine 中的 defer。
前面我们曾经介绍过 defer 关键字对应的 runtime.deferproc 会将延迟调用函数与调用方所在 goroutine 进行关联。所以当程序发生崩溃时只会调用当前 goroutine 的延迟调用函数也是非常合理的。
runtime.deferproc
goroutine1 =====> _defer ---> _defer -> _defer goroutine2 =====> _defer ---> _defer -> _defer goroutine3 =====> _defer ---> _defer -> _defer
像上面示意这样,每个 goroutine 都有自己的延迟调用链,相互之间没有太多的关联,一个 goroutine 在 panic 时也不应该执行其他 goroutine 的延迟函数。
初学 Go 语言的读者可能会写出下面的代码,在主程序中调用 recover 试图中止程序的崩溃,但是从运行的结果中我们也能看出,下面的程序没有正常退出。
func main() { defer fmt.Println("in main") if err := recover(); err != nil { fmt.Println(err) } panic("unknown err") } $ go run main.go in main panic: unknown err goroutine 1 [running]: main.main() ... exit status 2
仔细分析一下这个过程就能理解这种现象背后的原因,recover 只有在发生 panic 之后调用才会生效。然而在上面的控制流中,recover 是在 panic 之前调用的,并不满足生效的条件,所以我们需要在 defer 中使用 recover 关键字。
func main() { defer func() { fmt.Println("defer in main") if err := recover(); err != nil { fmt.Println(err) } }() panic("unknown err") }
Go 语言中的 panic 是可以多次嵌套调用的。一些熟悉 Go 语言的读者很可能也不知道这个知识点,如下所示的代码就展示了如何在 defer 函数中多次调用 panic:
func main() { defer fmt.Println("in main") defer func() { defer func() { panic("panic again and again") }() panic("panic again") }() panic("panic once") } $ go run main.go in main panic: panic once panic: panic again panic: panic again and again goroutine 1 [running]: ... exit code 2
从上述程序输出的结果,我们可以确定程序多次调用 panic 也不会影响 defer 函数的正常执行,所以使用 defer 进行收尾工作一般来说都是安全的。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
Go 语言中两个经常成对出现的两个关键字 — panic 和 recover。这两个关键字与上一节提到的 defer 有紧密的联系,它们都是 Go 语言中的内置函数,也提供了互补的功能。
panic和recover的一些行为现象
下面通过几个例子了解一下使用 panic 和 recover 关键字时遇到的这几个现象
panic 只能触发当前 goroutine 的 defer
panic 只会触发当前 Goroutine 的延迟函数调用,我们可以通过如下所示的代码了解该现象:
当我们运行这段代码时会发现 main 函数中的 defer 语句并没有执行,执行的只有当前 Goroutine 中的 defer。
前面我们曾经介绍过 defer 关键字对应的
runtime.deferproc
会将延迟调用函数与调用方所在 goroutine 进行关联。所以当程序发生崩溃时只会调用当前 goroutine 的延迟调用函数也是非常合理的。像上面示意这样,每个 goroutine 都有自己的延迟调用链,相互之间没有太多的关联,一个 goroutine 在 panic 时也不应该执行其他 goroutine 的延迟函数。
recover 只有在 defer 中调用才会生效
初学 Go 语言的读者可能会写出下面的代码,在主程序中调用 recover 试图中止程序的崩溃,但是从运行的结果中我们也能看出,下面的程序没有正常退出。
仔细分析一下这个过程就能理解这种现象背后的原因,recover 只有在发生 panic 之后调用才会生效。然而在上面的控制流中,recover 是在 panic 之前调用的,并不满足生效的条件,所以我们需要在 defer 中使用 recover 关键字。
嵌套panic
Go 语言中的 panic 是可以多次嵌套调用的。一些熟悉 Go 语言的读者很可能也不知道这个知识点,如下所示的代码就展示了如何在 defer 函数中多次调用 panic:
从上述程序输出的结果,我们可以确定程序多次调用 panic 也不会影响 defer 函数的正常执行,所以使用 defer 进行收尾工作一般来说都是安全的。
The text was updated successfully, but these errors were encountered: