天天看点

Go panic & recover 使用注意点:

1. panic:

一般表示程序出现了严重故障,此时程序不能继续运行,比如服务挂了...

产生方式/场景:

(1) 主动调用:通过 panic() 函数;

(2) 被动调用:panic产生后,会将堆栈信息抛出来,方便定位问题。

例如:程序在运行阶段发生了内存异常操作,例如:空指针的取值,索引越界,栈溢出,改写只读内存... 等等(还有其他情况会导致panic,借鉴简书大佬的链接:Go 常见的引发 panic 的情况)

注意点:

1. panic 其实是一个终止函数栈执行的过程,但是在函数退出前都会执行defer里面的函数,直到所有的函数都退出后,才会执行panic

2. recover:

recover是go提供的一个用来截获 panic 信息,重新获取协程控制的函数。

使用要求:

1)recover必须在defer函数中使用,但是不能被 defer 直接调用,如:

defer recover() // 直接调用,这样的写法不能恢复panic

2)recover只能恢复同一个协程中的panic,所以必须与可能发生panic的协程在同一个协程中才生效

错误示例:

panic在子协程中,而recover在主协程中,recover不能跨协程捕获panic信息,最终会导致所有的协程全部挂掉,程序会整体退出

package main

import (
	"fmt"
	"runtime/debug"
	"time"
)

// 错误示例:recover不能跨协程捕获panic信息
func Test() {
	panic("Test(): 我异常了...溜了溜了")
}

func main() {
	defer func() {
		err := recover(); if err != nil {
			debug.PrintStack()
			//fmt.Println(string(debug.Stack()))
		}
	}()

	go Test() // recover不能跨协程捕获
	// Test()
	time.Sleep(5 * time.Second) // 模拟执行耗时任务(顺便等待子协程执行)
	fmt.Println("main()不能正常执行...") // panic后,主协程main()无法继续正常执行打印
}
           

错误示例 - 运行结果:

Go panic & recover 使用注意点:

正确示例: 

package main

import (
	"fmt"
	"runtime/debug"
	"time"
)

// 正确示例:
func Test() {
	defer func() {
		if err := recover(); err != nil {
			debug.PrintStack()
			//fmt.Println(string(debug.Stack()))
		}
	}()
	panic("Test(): 我异常了...溜了溜了")
}

func main() {
	go Test()
	// Test()
	time.Sleep(5 * time.Second)       // 模拟执行耗时任务(顺便等待子协程执行)
	fmt.Println("main()依然是能正常执行的...") // 可以正常打印,即使Test()发生panic
}
           

正确示例 - 运行结果:

Go panic & recover 使用注意点:

3. 函数panic后,打印堆栈调用信息:

程序发生panic后,可以将堆栈信息打印出来,方便定位问题:

debug.PrintStack() 或 fmt.Println(string(debug.Stack()))

注意点:

1. 正确使用recover(),要记住recover只能恢复当前协程的panic,否则还是会导致整个进程挂掉

2. recover并非万能的,它只对用户态下的panic关键字有效

实际上在Go语言中,是存在着一些无法恢复的“恐慌”事件的,如fatalthrow方法、fatalpanic方法等,这些都是直接通过调用 exit() 方法进行中断的,属于无法恢复的“恐慌”事件,比如:

对于go1.6以上版本,如果出现 并发map读写 程序会直接以 fatal error 崩溃,即使在同一个协程内有recover()也不能恢复

  • 如果map由多协程同时读和写就会出现 fatal error:concurrent map read and map write的错误
  • 多个协程同时写会出现fatal error: concurrent map writes的错误

                                                                                     我写博客是真的慢...写了好久~