Go异常处理机制panic和recover

简介: Go异常处理机制panic和recover

recover


使用panic抛出异常后, 将立即停止当前函数的执行并运行所有被defer的函数,然后将panic抛向上一层,直至程序crash。但是也可以使用被defer的recover函数来捕获异常阻止程序的崩溃,recover只有被defer后才是有意义的。

func main() {
  print(123)
  print(456)
  panic("throw an error")
  print(678) //IDE会有提示: Unreachable code
}

结果:

123456panic: throw an error
goroutine 1 [running]:
main.main()
    /Users/shuangcui/explore/panicandrecover.go:31 +0x67

使用recover()捕获异常:

func main() {
  print(123)
  defer func() {
    if err := recover(); err != nil {
      print("recover it")
    }
  }()
  print(456)
  panic("throw an error")
  print(678) //IDE会有提示: Unreachable code
}

结果为:

123456recover it

如果有两个recover,则捕获异常的是后一个

func main() {
  print(123)
  defer func() {
    if err := recover(); err != nil {
      print("recover it")
    }
  }()
  defer func() {
    if err := recover(); err != nil {
      print("复原!")
    }
  }()
  print(456)
  panic("throw an error")
  print(678) //IDE会有提示: Unreachable code
}

结果为:

123456复原!

panic之后的任何代码都不会继续执行


前提是panic不在if里面

package main
import "fmt"
func main() {
  defer_call()
  fmt.Println("333 Helloworld")
}
func defer_call() {
  defer func() {
    fmt.Println("11111")
  }()
  defer func() {
    fmt.Println("22222")
  }()
  defer func() {
    if r := recover(); r != nil {
      fmt.Println("Recover from r : ", r)
    }
  }()
  defer func() {
    fmt.Println("33333")
  }()
  fmt.Println("111 Helloworld")
  panic("Panic 1!")
    //使用panic抛出异常后, 将立即停止当前函数的执行并运行所有被defer的函数,然后将panic抛向上一层, 直至程序crash
    //但是也可以使用被defer的recover函数来捕获异常阻止程序的崩溃,recover只有被defer后才是有意义的。
  panic("Panic 2!") //panic1之后的panic2没有任何机会会被执行, panic2之后的任何代码更没有任何机会被执行
  fmt.Println("222 Helloworld")
}

输出为:

111 Helloworld
33333
Recover from r :  Panic 1!
22222
11111
333 Helloworld

对于goroutine中的panic,协程外面的recover是无法恢复的;goroutine中的recover,同样无法恢复协程外的panic

微信截图_20230814204527.png

但协程中的recover可以恢复协程中的panic

package main
import (
  "fmt"
  "time"
)
func main() {
  go func() {
    defer func() {
      if err := recover(); err != nil {
        fmt.Println("recover err:", err)
      }
    }()
    panic("里面出错了")
  }()
  //panic("外面出错了")
  time.Sleep(1 * time.Second)
}

输出为:

recover err 里面出错了


主方法中的recover,也可以恢复子方法里的panic


但如果go subfunc(),则同样无法捕获subfunc中的异常

func main() {
  fmt.Println(123)
  defer fmt.Println(999)
  defer func() {
    if err := recover(); err != nil {
      fmt.Println("恢复异常:",err)
    }
  }()
  subfunc()
}
func subfunc() {
  defer fmt.Println(888)
  panic("出现了bug")
  defer fmt.Println(456)
}

结果为:

123
888
恢复异常: 出现了bug
999

因为panic发生的时候,panic函数后面的语句都不会执行了,所以recover函数不能放在panic语句后面执行,而要放在defer函数中执行。

使用 panic 抛出异常后,函数执行将从调用 panic 的地方停止,如果函数内有 defer 调用,则执行 defer 后边的函数调用,如果 defer 调用的函数中没有捕获异常信息,这个异常会沿着函数调用栈往上传递,直到 main 函数仍然没有捕获异常,将会导致程序异常退出


如何区别使用 panic 和 error 两种方式?

惯例是:导致关键流程出现不可修复性错误的使用 panic ,其他使用 error 。

panic 和 recover 的组合有如下特性:

  • 有 panic 没 recover ,程序宕机。
  • 有 panic 也有 recover ,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行。




recover能捕获所有错误吗?


不能!

Go 有哪些无法恢复的致命场景?

  • 并发读写 map fatal error: concurrent map read and map write
  • 堆栈内存耗尽(如递归)
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200e1bf0 stack=[0xc0200e0000, 0xc0400e0000]
fatal error: stack overflow
  • 将 nil 函数作为 goroutine 启动 fatal error: go of nil func value
  • goroutines 死锁  fatal error: all goroutines are asleep - deadlock!
  • 线程超过设置的最大限制  fatal error: thread exhaustion
  • 超出可用内存 fatal error: runtime: out of memory

总之 都会报fatal error:xxxxxxxx


拓展&参考:

golang panic和recover 实现原理

Go 学习笔记(19)— 函数(05)[如何触发 panic、触发 panic 延迟执行、panic 和 recover 的关系]

Go 语言踩坑记——panic 与 recover


目录
相关文章
|
11月前
|
Go 调度
GO语言函数的内部运行机制分析
以上就是Go语言中函数的内部运行机制的概述,展示了函数在Go语言编程中如何发挥作用,以及Go如何使用简洁高效的设计,使得代码更简单,更有逻辑性,更易于理解和维护。尽管这些内容深入了一些底层的概念,但我希望通过这种方式,将这些理论知识更生动、更形象地带给你,让你在理解的同时找到编程的乐趣。
210 5
|
11月前
|
安全 Go
defer关键字:延迟调用机制-《Go语言实战指南》
Go 语言中的 `defer` 是用于延迟执行函数调用的关键字,广泛应用于资源释放、异常捕获和日志记录等场景。它在函数返回前执行,支持栈式后进先出(LIFO)顺序,参数求值时机为声明时而非执行时。常见用法包括文件关闭、锁解锁及结合 `recover` 处理 panic。尽管高效,频繁使用可能带来性能开销,需谨慎处理。总结而言,`defer` 是构建健壮代码的核心工具之一。
|
存储 算法 物联网
解析局域网内控制电脑机制:基于 Go 语言链表算法的隐秘通信技术探究
数字化办公与物联网蓬勃发展的时代背景下,局域网内计算机控制已成为提升工作效率、达成设备协同管理的重要途径。无论是企业远程办公时的设备统一调度,还是智能家居系统中多设备间的联动控制,高效的数据传输与管理机制均构成实现局域网内计算机控制功能的核心要素。本文将深入探究 Go 语言中的链表数据结构,剖析其在局域网内计算机控制过程中,如何达成数据的有序存储与高效传输,并通过完整的 Go 语言代码示例展示其应用流程。
240 0
|
存储 安全 Go
Go 语言以其高效的并发编程能力著称,主要依赖于 goroutines 和 channels 两大核心机制
Go 语言以其高效的并发编程能力著称,主要依赖于 goroutines 和 channels 两大核心机制。本文介绍了这两者的概念、用法及如何结合使用,实现任务的高效并发执行与数据的安全传递,强调了并发编程中的注意事项,旨在帮助开发者更好地掌握 Go 语言的并发编程技巧。
262 2
|
存储 编译器 Go
|
Go 数据处理
实验深度理解Go中try...catch...的panic、defer、recover用法
文章通过实验代码演示了Go语言中如何使用panic、defer和recover函数来模拟try...catch...的异常处理机制,并详细解释了每个函数的作用和在异常处理中的使用场景。
291 0
|
6月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
351 2