exit()
捕获错误panic、打印错误栈
func TestPanic(t *testing.T) {
j := 0
for i := 0; i <= 10; i++ {
func() { // try except
defer func() {
if err := recover(); err != nil {
fmt.Println("panic recover!")
// 打印错误和堆栈
fmt.Printf("err=%v, stack=%s\n", err, string(debug.Stack()))
}
}()
fmt.Printf("%v\n", i)
x := i / j // 除0报错
fmt.Printf("%v", x)
}()
}
}
以上代码,通过匿名函数把defer和报错代码放到一个协程里,让recover能捕获到错误。
panic只会触发当前goroutine的defer,recover只有在defer中调用才能生效,panic允许在defer中嵌套多次调用。
defer跨协程失效
func TestPanic1(t *testing.T) {
// 跨协程失效,下面一行的defer不会执行
defer println("in main")
go func() {
defer println("in goroutine")
fmt.Println("子协程running")
panic("子协程崩溃")
}()
time.Sleep(1 * time.Second)
}
子协程里defer的和主协程里的defer不在一个协程里,所以子协程panic后,主线程中的defer并不会执行。
recover只在发生panic之后调用才会生效
func TestPanic2(t *testing.T) {
defer fmt.Println("in main")
if err := recover(); err != nil {
fmt.Println("occur error")
fmt.Println(err)
}
panic("unknown err")
}
以上代码中,发送错误之前,已经调用了recover,导致recover无法捕获错误,正确写法是放到defer里,延迟调用。
func TestPanic3(t *testing.T) {
defer fmt.Println("in main")
defer func() {
if err := recover(); err != nil {
fmt.Println("occur error")
fmt.Println(err)
}
}()
panic("unknown err")
}
recover的作用是捕获panic,从而恢复正常代码执行。
recover必须配合defer使用。
recover没有传入参数,但是有返回值,返回值就是panic传递的值。
panic嵌套调用顺序
func TestPanic4(t *testing.T) {
defer fmt.Println("in main")
defer func() {
defer func() {
panic("panic again and again")
}()
panic("panic again")
}()
// 主动触发panic
panic("panic once")
}
// in main
// --- FAIL: TestPanic4 (0.00s)
// panic: panic once
// panic: panic again
// panic: panic again and again [recovered]
// panic: panic again and again
如果函数F中书写并触发了panic语句,会终止其后要执行的代码。在panic所在函数F内如果存在要执行的defer函数列表,则按照defer书写顺序的逆序执行;
如果函数G调用函数F,则函数F panic后返回调用者函数G。函数G中,调用函数F语句之后的语句都不会执行。假如函数G中也有要执行的defer函数列表,则按照defer书写顺序的逆序子还行;
退出整个goroutine,并报告错误。
recover只会捕获最后一个panic
func TestPanic5(t *testing.T) {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer func() {
panic("three")
}()
defer func() {
panic("two")
}()
panic("one")
// three
}
常见的触发panic情况
数组下标越界(运行时错误)
func TestPanic6(t *testing.T) {
var s []string
fmt.Println(s)
fmt.Println(s[0])
// panic: runtime error: index out of range [0] with length 0 [recovered]
// panic: runtime error: index out of range [0] with length 0
}
空指针异常(运行时错误)
func TestPanic7(t *testing.T) {
type Person struct {
Name string
Age int
}
var p *Person
fmt.Println(p)
fmt.Println(p.Name)
// panic: runtime error: invalid memory address or nil pointer dereference [recovered]
// panic: runtime error: invalid memory address or nil pointer dereference
//
// [signal 0xc0000005 code=0x0 addr=0x0 pc=0xafe05b]
}
类型断言失败(接口转换异常)
func add(a, b interface{}) {
i := a.(int)
j := b.(int)
fmt.Println(i + j)
}
func TestPanic8(t *testing.T) {
add(20, 18)
add(1, "hello")
// 38
//
// --- FAIL: TestPanic8 (0.00s)
// panic: interface conversion: interface {} is string, not int [recovered]
//
// panic: interface conversion: interface {} is string, not int
}
通道为空,通道已关闭(写数据)
func TestPanic9(t *testing.T) {
var ch chan int
close(ch)
// panic: close of nil channel [recovered]
// panic: close of nil channel
}