解释一下Go recover的执行时机 ?

参考回答

在 Golang 中,recover 的执行时机是在 defer 函数中捕获到 panic 才会执行。如果没有发生 panic 或者 recover 不在 defer 中调用,它不会生效。

recover 的作用是捕获 panic,从而阻止程序崩溃,允许程序继续运行或进行清理操作。它的典型使用场景是异常处理。


详细讲解与拓展

1. panicrecover 的机制

  • panic:用来主动中断程序执行,类似于抛出异常。当发生 panic 时,当前函数会立刻停止执行,并将控制权传递到调用链上的 defer 语句。
  • recover:用来捕获 panic,可以恢复程序的正常执行。

注意recover 只有在 defer 中调用时,才能捕获到当前的 panic

2. 执行顺序和示例

panic 发生时,以下顺序执行:
1. 停止当前函数的执行。
2. 调用已经定义的 defer 语句。
3. 如果在某个 defer 中调用了 recover,并成功捕获 panic,程序会恢复执行,panic 不会继续传播。
4. 如果没有 recoverpanic 会沿调用链向上传播,最终导致程序崩溃。

示例 1:捕获 panic

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()

    fmt.Println("Before panic")
    panic("Something went wrong!")
    fmt.Println("After panic") // 不会执行
}

输出:

Before panic
Recovered from panic: Something went wrong!

解释
1. panic("Something went wrong!") 触发 panic
2. 调用 defer 中的匿名函数。
3. recover 捕获了 panic,程序恢复正常。


3. recover 的使用场景

(1) 防止程序崩溃
– 在关键逻辑中使用 recover,确保即使出现异常也不会导致整个程序崩溃。

示例:

“`go
func safeDivide(a, b int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
fmt.Println(a / b) // 如果 b == 0,会引发 panic
}

func main() {
safeDivide(10, 0) // 捕获 panic
fmt.Println("Program continues running")
}

“`
输出:
“`
Recovered from panic: runtime error: integer divide by zero
Program continues running
“`

(2) 用于服务稳定性
– 在 Web 服务中,recover 可以避免因单个请求引发的异常导致整个服务崩溃。

示例:

“`go
func handler() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in handler:", r)
}
}()
panic("Handler panic!")
}

func main() {
handler()
fmt.Println("Server keeps running")
}

“`


4. recover 的注意事项

  1. 必须在 defer 中调用
    如果直接调用 recover() 而不在 defer 中,是无法捕获 panic 的。

    func main() {
       fmt.Println(recover()) // 输出:<nil>
    }
    
  2. 无法捕获 goroutine 中的 panic
    如果 panic 发生在独立的 goroutine 中,而没有在该 goroutine 内使用 recover,则 panic 会导致该 goroutine 崩溃。

    func main() {
       go func() {
           defer func() {
               if r := recover(); r != nil {
                   fmt.Println("Recovered in goroutine:", r)
               }
           }()
           panic("Goroutine panic!")
       }()
    
       // 主 goroutine 不会受到影响
       fmt.Println("Main goroutine continues")
    }
    
  3. 恢复之后的程序行为
    recover 捕获 panic 后,程序会从 panic 发生点最近的 defer 处继续执行。


总结

  1. 执行时机

    • recover 必须在 defer 中调用,并且只有发生了 panic 时才会生效。
    • 发生 panic 后,函数停止执行,defer 的代码块会被调用,recover 捕获异常后可以恢复程序执行。
  2. 使用场景
    • 用于异常处理,防止程序崩溃。
    • 在 Web 服务、任务调度中使用 recover 提高系统稳定性。
  3. 注意事项
    • 无法捕获 goroutine 外的 panic
    • 如果不在 defer 中调用,recover 不会生效。

理解 panicrecover 的执行时机和原理,有助于更好地编写高健壮性的代码。

发表评论

后才能评论