panic

数组越界, 空指针引用等, 这些运行时错误会引起 panic 异常

:::info 我们不应该通过调用 panic 函数来报告普通错误, 而应该只把他作为报告致命错误的一种方式.

:::

当某些不应该发生的场景发生时, 我们就应该调用 panic

一般而言, 当 panic 异常发生时, 程序会中断运行, 并立即执行在该 goroutine(一种线程在中被延迟的函数 (defer 机制)). 随后, 程序崩溃并输出日志信息. 日志信息包括 panic value 和函数调用的堆栈跟踪信息

不是所有的 panic 异常都来自运行时, 直接调用内置的 panic 函数也会引发 panic 异常, panic 函数接收任何值作为参数

  1. func panic(v interface{})

虽然Go语言的 panic 机制类似于其他语言的异常,但 panic 的适用场景有一些不同,由于 panic 会引起程序的崩溃,因此 panic 一般用于严重错误,如程序内部的逻辑不一致。任何崩溃都表明了我们的代码中可能存在漏洞,所以对于大部分漏洞,我们应该使用Go语言提供的错误机制,而不是 panic。

手动触发宕机

Go 语言可以在程序中手动触发宕机,让程序崩溃,这样开发者可以及时地发现错误,同时减少可能的损失。

Go 语言程序在宕机时,会将堆栈和 goroutine 信息输出到控制台,所以宕机也可以方便地知晓发生错误的位置,那么我们要如何触发宕机呢,示例代码如下所示:

  1. package main
  2. func main() {
  3. panic("crash")
  4. }

代码运行崩溃并输出如下:

  1. panic: crash
  2. goroutine 1 [running]:
  3. main.main()
  4. D:/code/main.go:4 +0x40
  5. exit status 2

以上代码中只用了一个内建的函数 panic() 就可以造成崩溃,panic()的声明如下:

  1. func panic(v interface{}) //panic() 的参数可以是任意类型的。

在宕机时触发延迟执行语句

panic() 触发的宕机发生时,panic() 后面的代码将不会被运行,但是在 panic() 函数前面已经运行过的 defer 语句依然会在宕机发生时发生作用,参考下面代码:

  1. package main
  2. import "fmt"
  3. func main() {
  4. defer fmt.Println("宕机后要做的事情1")
  5. defer fmt.Println("宕机后要做的事情2")
  6. panic("宕机")
  7. }

代码输出如下:

  1. 宕机后要做的事情 2
  2. 宕机后要做的事情 1
  3. panic: 宕机
  4. goroutine 1 [running]:
  5. main.main()
  6. D:/code/main.go:8 +0xf8
  7. exit status 2

对代码的说明:

  • 第 6 行和第 7 行使用 defer 语句延迟了 2 个语句。
  • 第 8 行发生宕机。

宕机前,defer 语句会被优先执行,由于第 7 行的 defer 后执行,因此会在宕机前,这个 defer 会优先处理,随后才是第 6 行的 defer 对应的语句,这个特性可以用来在宕机发生前进行宕机信息处理。