go中的panic和recover

182 阅读1分钟

panic和recover用于将异常的处理和正常的业务逻辑代码解耦,如果没有recover,panic时程序将会异常终止。panic只会调用当前协程的defer,从而也只会调用当前协程的recover。

  1. panic调用defer的顺序
package main

import "fmt"

func defer1() {
	fmt.Println("defer1")
}

func defer2() {
	fmt.Println("defer2")
}

func main() {
	defer defer1()
	defer defer2()
	fmt.Println("in main")
	panic("panic occured")
}

输出

in main
defer2
defer1
panic: panic occured

goroutine 1 [running]:
main.main()
        C:/Users/wangfeng/go/src/awesomeProject/panic.go:17 +0xcb
exit status 2

panic之前按照出栈顺序调用defer。

  1. panic只会调用当前协程的defer
package main

import (
	"fmt"
	"sync"
)

func defer1() {
	fmt.Println("defer1")
}

func defer2() {
	fmt.Println("defer2")
}

func goroutine(wg *sync.WaitGroup) {
	defer wg.Done()
	defer defer2()
	fmt.Println("i am goroutine.")
	panic("panic occured in goroutine")
}

func main() {
	var wg sync.WaitGroup

	defer defer1()
	fmt.Println("in main")
	wg.Add(1)
	go goroutine(&wg)
	wg.Wait()

}

输出

C:\Users\wangfeng\go\src\awesomeProject>go run panic.go
in main
i am goroutine.
defer2
panic: panic occured in goroutine

goroutine 6 [running]:
main.goroutine(0xc00000a0b0)
        C:/Users/wangfeng/go/src/awesomeProject/panic.go:20 +0xe5
created by main.main
        C:/Users/wangfeng/go/src/awesomeProject/panic.go:29 +0xf5
exit status 2

panic返回之前,只会调用当前协程的defer。没有recover时,panic会退出整个进程。

  1. panic和recover
package main

import (
	"fmt"
	"sync"
)

func defer1() {
	fmt.Println("defer1")
}

func defer2() {
	fmt.Println("defer2")
	if err:=recover(); err!=nil {
		fmt.Println("in recover()", err)
	}
}

func goroutine(wg *sync.WaitGroup) {
	defer wg.Done()
	defer defer2()
	fmt.Println("i am goroutine.")
	panic("panic occured in goroutine")
}

func main() {
	var wg sync.WaitGroup

	defer defer1()
	fmt.Println("in main")
	wg.Add(1)
	go goroutine(&wg)
	wg.Wait()

}

输出

C:\Users\wangfeng\go\src\awesomeProject>go run panic.go
in main
i am goroutine.
defer2
in recover() panic occured in goroutine
defer1

recover会让进程正常终止,类似try catch。

  1. panic之后的不会执行
package main 

import "fmt"

func recover1() {
  if err:=recover(); err!=nil {
  	fmt.Println("recover1", err)
  }
}

func recover2() {
  if err:=recover(); err!=nil {
  	fmt.Println("recover2", err)
  }
}

func worker(){
  defer recover2()

  fmt.Println("i am worker")
  panic("worker error")
}

func main(){
  defer recover1()
  
  panic("main error")
  
  go worker()
}

输出:

recover1 main error