go 的异常处理:panic 和 recover

panic 函数 和 recover 函数

panicrecover 在使用方法上更接近于 try/catch 结构化异常:

func panic(v interface{})
func recover() interface{}
panic
  • go 中协程是平等的,如果一个协程中产生的 panic 没有处理掉,会导致整个程序奔溃

    package main
    
    func main() {
      go func() {
          panic("hello world")
      }()
    
      select {}
    }
    
    image.png
  • panic 会递归的执行本协程中所有的defer,与函数正常退出时执行的顺序一致

    func main() {
        defer fmt.Print("A")
        defer fmt.Print("B")
        fmt.Print("C")
        panic("demo")
        defer fmt.Print("D")
    }
    
    image.png
  • panic 不会调用其他协程中的defer

    func foo() {
        defer fmt.Print("A")
        defer fmt.Print("B")
        
        fmt.Print("C")
        panic("foo demo")
        defer fmt.Print("D")
    }
    
    func main() {
        defer func () {  fmt.Print("main demo")  }()
        go foo()
        select {}  // 防止main协程提前退出
    }
    
    image.png
  • 如果panic在执行过程再次发生panic,程序立即终止当前的 defer函数的执行,然后继续接下来的panic流程,只是当前的defer函数中的panic后面的语句就没有机会执行了

    func main() {
        defer func() { recover() }()
    
        defer fmt.Println("A")
        defer func() {
            fmt.Print("B")
            panic("panic in defer")
            fmt.Print("C")
        }()
    
        panic("panic")
        fmt.Print("D")
    }
    
    image.png

    注意 C 没有输出。

recover
  • 当函数中发生panic 并用 recover 恢复后,无法回到本函数发生panic的位置继续执行, 同时如果本函数有匿名返回值,则直接返回相应类型的零值,对于具名返回值,函数将返回当前已经存在的值。

    func foo() {
        defer fmt.Println("C")
        defer func() {
            if err := recover(); err != nil {
                fmt.Print("A")
            }
        }()
    
        panic("demo")
        fmt.Println("B")
    }
    
    func main() {
        foo()
    }
    
    image.png

    注意没有输出B

  • panic 被 recover 之后,无法再次被 recover 捕获

    func foo() {
        defer func() {
            if err := recover(); err != nil {
                fmt.Print("A")
            }
            fmt.Print("a")
        }()
    
        defer func() {
            if err := recover(); err != nil {
                fmt.Print("B")
            }
        }()
    
        panic("demo")
        fmt.Print("B")
    }
    
    func main() {
        foo()
    }
    
    image.png
  • recover 函数必须且直接位于defer函数中才有效
    func main() {
        defer func() {
            func() {
                if err := recover(); err != nil {
                    fmt.Println("A")
                }
            }()
        }()
    
        panic("demo")
        fmt.Println("B")
    }
    

    上面这种写法就不会生效。


    image.png

你可能感兴趣的:(go 的异常处理:panic 和 recover)