go panic和recover

概念

现象

  1. panic 只会触发当前 Goroutine 的 defer

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func main() {
    	defer fmt.Println("主函数defer执行...")
    
    	go func() {
    		defer fmt.Println("子函数defer执行...")
    		panic("子函数报错...")
    	}()
    
    	time.Sleep(time.Second)
    	fmt.Println("正常业务代码...")
    }
    
    
    go run main.go
    子函数defer执行...
    panic: 子函数报错...
    
    goroutine 18 [running]:
    main.main.func1()
    	/Users/user/go/src/go-demo/defer/main.go:13 +0x73
    created by main.main
    	/Users/user/go/src/go-demo/defer/main.go:11 +0x79
    exit status 2
    
  2. recover 只有在 defer 中调用才会生效

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	defer fmt.Println("主函数defer执行...")
    
    	if err := recover(); err != nil {
    		fmt.Println("进入recover...")
    	}
    
    	panic("主函数报错...")
    }
    
    go run main.go
    主函数defer执行...
    panic: 主函数报错...
    
    goroutine 1 [running]:
    main.main()
    	/Users/user/go/src/go-demo/defer/main.go:14 +0x8d
    exit status 2
    

    上面的 recover 并不会生效,因为 recover 只有在 panic 之后才会调用,上面的代码会先执行 recover,所以不会生效。如果想使 recover 生效,就必须放到 defer 中。

    我们修改下上面的代码:

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	defer func() {
    		fmt.Println("主函数defer执行...")
    		if err := recover(); err != nil {
    			fmt.Println("进入recover...")
    			fmt.Println(err)
    		}
    	}()
    
    	panic("主函数报错...")
    }
    
    go run main.go
    主函数defer执行...
    进入recover...
    主函数报错...
    
  3. panic 允许在 defer 中嵌套多次调用

    package main
    
    func main() {
    	defer func() {
    		defer func() {
    			panic("defer defer panic")
    		}()
    		panic("defer panic")
    	}()
    
    	panic("主函数报错...")
    }
    
    go run main.go
    panic: 主函数报错...
    	panic: defer panic
    	panic: defer defer panic
    
    goroutine 1 [running]:
    main.main.func1.1()
    	/Users/user/go/src/go-demo/defer/main.go:6 +0x27
    panic({0x1059120, 0x1075b38})
    	/usr/local/go/src/runtime/panic.go:1038 +0x215
    main.main.func1()
    	/Users/user/go/src/go-demo/defer/main.go:8 +0x49
    panic({0x1059120, 0x1075b18})
    	/usr/local/go/src/runtime/panic.go:1038 +0x215
    main.main()
    	/Users/user/go/src/go-demo/defer/main.go:11 +0x49
    exit status 2