Go 编程 | 连载 26 - Go 的 panic 与 recover

1,712 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情

一、panic 和 recover

Go 语言中的错误处理机制是如果一个可能造成错误的函数,需要在该函数的返回值中增加一个错误接口。如果函数调用成功,错误接口将被返回 nil,如果调用失败,则返回具体的错误内容。

在函数调用后需要检查错误,进行必要的错误处理。

Go 编程 | 连载 19 - 接口的应用 讲到了实例化错误类型的多种方式,这里不再赘述。

panic

panic 在 Go 语言中表示是宕机,既服务停止或者程序终止运行。

Go 语言中可以手动触发 panic,让程序终止运行,这样可以在程序发生错误时及时止损。panic 发生时会将堆栈和 goroutine 信息输出到控制台,可以据此知晓发生错误的位置,如果在编译时加入的调试信息甚至连 panic 发生时的变量运行状态都可以获得。

func main() {
   
   var info any
   info = "crash"
   panic(info)
   
   fmt.Println("Hello Go")

}

执行上述代码,输出结果如下:

panic: crash

goroutine 1 [running]:
main.main()
	/ex25.go:6 +0x27

只通过一个 panic 函数就是程序终止运行,panic 函数后面的代码也没有执行。panic 函数是 Go 的内置函数,该函数的参数是一个 any 类型,而 any 是一个接口。

当 panic 发生时,panic 函数后的代码将不会被执行,但是 panic 函数前面已经运行过的 defer 语句仍然会在 panic 发生时运行。

func main() {

   defer fmt.Println("panic 前")
   var info any
   info = "crash"
   panic(info)

   fmt.Println("Hello Go")
   defer fmt.Println("panic 后")
}

执行上述代码,输出结果如下:

panic 前
panic: crash

goroutine 1 [running]:
main.main()
	/ex25.go:10 +0x73

panic 发生前,在 panic 函数前面的 defer 会优先执行,但是在 panic 函数后的 defer 不会被执行,因为 panic 发生程序停止服务,包括 defer 语句所有的代码都不会被执行。

recover

代码运行时的发生的错误可以通过 deferrecover 实现错误捕捉和恢复。让代码发生在泵阔后允许继续运行,类似其他语言中的 try-catch

Go 中没有异常的概念,只有错误,panic 函数触发宕机就类似于其他语言中的异常。

recover 函数的返回值为任意内容

func recover() any
package main

//noinspection ALL
import (
   "fmt"
   "runtime"
)

type panicContent struct {
   function string
}

//noinspection ALL
func main() {

   fmt.Println("手动触发 painc 前")

   samFun(func() {
      panic(&panicContent{
         "手动触发 panic",
      })
   })

   fmt.Println("手动触发 painc 后")

   fmt.Println("空指针导致 panic")

   samFun(func() {
      fmt.Println("赋值前")

      var a *int
      *a = 1

      fmt.Println("赋值后")
   })

   fmt.Println("panic 后")
}

func samFun(f func()) {

   defer func() {
      // 发生 panic 时,获取 panic 函数传递的上下文并打印
      err := any(recover())

      switch err.(type) {
      case runtime.Error:
         fmt.Println("runtime error:", err)
      default:
         fmt.Println("error:",err)
      }

   }()

   f()
}

执行上述代码,输出结果如下:

手动触发 painc 前
error: &{手动触发 panic}
手动触发 painc 后
空指针导致 panic
赋值前
runtime error: runtime error: invalid memory address or nil pointer dereference
panic 发生后

panic 函数和 recover 函数同时存在,程序不会宕机,执行对应的 defer 语句后,从宕机点退出当前函数后继续执行。

二、panic 和 recover 的坑

image.png

Go 中错误与异常的区分

image.png

image.png

image.png

image.png

go 中的 ”try catch“ 这默写

image.png

这默写是不行的

image.png

panic 中的坑

先写一个 http server

image.png

image.png

image.png 出现异常,服务未停

开启一个协程,在携程中抛出异常 image.png 出现异常,服务停止

image.png

为什么在主线程中 panic 不会报错导致服务停止,在携程中 panic 就会导致服务停止

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

在当前携程中捕获异常,防止出现报错

image.png

image.png