go recover 异常捕获

越界异常

发生异常

func TestOverstep(t *testing.T){
	var a [10]int
	i:=20
	t.Log("test over step recover")
	a[i] = 111 //当x为20时候,导致数组越界,产生一个panic,导致程序崩溃
	t.Log("end test over step")
}

输出结果:

D:\xzw\demo\go\异常捕获>go test -v -run TestOverstep recover_test.go
=== RUN   TestOverstep
--- FAIL: TestOverstep (0.00s)
    recover_test.go:13: test over step recover
panic: runtime error: index out of range [recovered]
        panic: runtime error: index out of range

goroutine 5 [running]:
testing.tRunner.func1(0xc0000ac100)
        D:/software/go/src/testing/testing.go:830 +0x399
panic(0x522fe0, 0x641fe0)
        D:/software/go/src/runtime/panic.go:522 +0x1c3
command-line-arguments.TestOverstep(0xc0000ac100)
        D:/xzw/demo/go/异常捕获/recover_test.go:14 +0x75
testing.tRunner(0xc0000ac100, 0x554a20)
        D:/software/go/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
        D:/software/go/src/testing/testing.go:916 +0x361
FAIL    command-line-arguments  0.697s

捕获异常

// 数组越界捕获
func TestOverstepRecover(t *testing.T){
	defer func() {
		err := recover() //获取异常
		if err != nil {
			t.Fatal("error: " + fmt.Sprintf("%s", err))
		}
	}()
	var a [10]int
	i:=20
	t.Log("test over step recover")
	a[i] = 111 //当x为20时候,导致数组越界,产生一个panic,导致程序崩溃
	t.Log("end test over step recover")
	time.Sleep(3*time.Second)
}

输出结果:

D:\xzw\demo\go\异常捕获>go test -v -run TestOverstepRecover recover_test.go
=== RUN   TestOverstepRecover
--- FAIL: TestOverstepRecover (0.00s)
    recover_test.go:27: test over step recover
    recover_test.go:22: error: runtime error: index out of range
FAIL
FAIL    command-line-arguments  0.614s

除0异常

func TestZeroRecover(t *testing.T){
	defer func() {
		err := recover() //获取异常
		if err != nil {
			t.Fatal("error: " + fmt.Sprintf("%s", err))
		}
	}()

	t.Log("start test !")
	i :=0
	r := 5/i
	t.Logf("end test %d!",r)
	time.Sleep(3*time.Second)
}

输出结果:

D:\xzw\demo\go\异常捕获>go test -v -run TestZeroRecover recover_test.go
=== RUN   TestZeroRecover
--- FAIL: TestZeroRecover (0.00s)
    recover_test.go:42: start test !
    recover_test.go:38: error: runtime error: integer divide by zero
FAIL
FAIL    command-line-arguments  0.593s

map 并发读写错误

func TestMapRecover(t *testing.T){

	ageMp := make(map[string]int, 8)
	t.Log("start test !")
	go func() {
		defer func() {
			err := recover() //获取异常
			if err != nil {
				t.Fatal("error: " + fmt.Sprintf("%s", err))
			}
		}()
		for{
			ageMp["xzw"] = 10
		}
	}()
	go func() {
		defer func() {
			err := recover() //获取异常
			if err != nil {
				t.Fatal("error: " + fmt.Sprintf("%s", err))
			}
		}()
		for{
			ageMp["xzw"] = 11
		}
	}()

	t.Log("end test ")
	time.Sleep(3000*time.Second)
}

输出结果:

D:\xzw\demo\go\异常捕获>go test -v -run TestMapRecover recover_test.go
=== RUN   TestMapRecover
fatal error: concurrent map writes

goroutine 20 [running]:
runtime.throw(0x54efda, 0x15)
        D:/software/go/src/runtime/panic.go:617 +0x79 fp=0xc0000d3f30 sp=0xc0000d3f00 pc=0x42e449
runtime.mapassign_faststr(0x521f00, 0xc000066510, 0x54b180, 0x3, 0xc0000d4088)
        D:/software/go/src/runtime/map_faststr.go:211 +0x431 fp=0xc0000d3f98 sp=0xc0000d3f30 pc=0x411e21
command-line-arguments.TestMapRecover.func1(0xc0000c6100, 0xc000066510)
        D:/xzw/demo/go/异常捕获/recover_test.go:61 +0x7d fp=0xc0000d3fd0 sp=0xc0000d3f98 pc=0x4ff0dd
runtime.goexit()
        D:/software/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc0000d3fd8 sp=0xc0000d3fd0 pc=0x459411
created by command-line-arguments.TestMapRecover
        D:/xzw/demo/go/异常捕获/recover_test.go:53 +0xae

goroutine 1 [chan receive]:
testing.(*T).Run(0xc0000c6100, 0x54cf61, 0xe, 0x555a98, 0x473c01)
        D:/software/go/src/testing/testing.go:917 +0x388
testing.runTests.func1(0xc0000c6000)
        D:/software/go/src/testing/testing.go:1157 +0x7f
testing.tRunner(0xc0000c6000, 0xc00008be30)
        D:/software/go/src/testing/testing.go:865 +0xc7
testing.runTests(0xc000058460, 0x6482c0, 0x7, 0x7, 0x0)
        D:/software/go/src/testing/testing.go:1155 +0x2b0
testing.(*M).Run(0xc0000ae000, 0x0)
        D:/software/go/src/testing/testing.go:1072 +0x169
main.main()
        _testmain.go:54 +0x145

goroutine 19 [sleep]:
runtime.goparkunlock(...)
        D:/software/go/src/runtime/proc.go:307
time.Sleep(0x2ba7def3000)
        D:/software/go/src/runtime/time.go:105 +0x16e
command-line-arguments.TestMapRecover(0xc0000c6100)
        D:/xzw/demo/go/异常捕获/recover_test.go:77 +0x137
testing.tRunner(0xc0000c6100, 0x555a98)
        D:/software/go/src/testing/testing.go:865 +0xc7
created by testing.(*T).Run
        D:/software/go/src/testing/testing.go:916 +0x361

goroutine 21 [runnable]:
command-line-arguments.TestMapRecover.func2(0xc0000c6100, 0xc000066510)
        D:/xzw/demo/go/异常捕获/recover_test.go:72 +0x7d
created by command-line-arguments.TestMapRecover
        D:/xzw/demo/go/异常捕获/recover_test.go:64 +0xda
FAIL    command-line-arguments  0.598s

注:没有捕获到异常,是因为Go 语言系统级别的错误,比如发生死锁,数据竞争,这种错误程序会立刻报错,无法 recover。

嵌套异常捕获

func zero(t *testing.T){
	i :=0
	r := 5/i
	t.Logf("end zero %d!",r)
}
//嵌套异常捕获
func TestPanicNestedRecover(t *testing.T){
	defer func() {
		err := recover() //获取异常
		if err != nil {
			t.Fatal("error: " + fmt.Sprintf("%s", err))
		}
	}()

	t.Log("start test !")
	zero(t)
	t.Logf("end test!")
	time.Sleep(3*time.Second)
}

输出结果:

D:\xzw\demo\go\异常捕获>go test -v -run TestPanicNestedRecover recover_test.go
=== RUN   TestPanicNestedRecover
--- FAIL: TestPanicNestedRecover (0.00s)
    recover_test.go:87: start test !
    recover_test.go:83: error: runtime error: integer divide by zero
FAIL
FAIL    command-line-arguments  0.580s

子线程异常捕获

func TestRoutinePanic(t *testing.T){
	defer func() {
		err := recover() //获取异常
		if err != nil {
			t.Fatal("error: " + fmt.Sprintf("%s", err))
		}
	}()

	t.Log("start test !")
	go  zero(t)

	time.Sleep(3*time.Second)
}

输出结果:

\xzw\demo\go\异常捕获>go test -v -run TestRoutinePanic recover_test.go
=== RUN   TestRoutinePanic
panic: runtime error: integer divide by zero

goroutine 20 [running]:
command-line-arguments.zero(0xc0000c8100)
        D:/xzw/demo/go/异常捕获/recover_test.go:75 +0x11
created by command-line-arguments.TestRoutinePanic
        D:/xzw/demo/go/异常捕获/recover_test.go:102 +0xc0
FAIL    command-line-arguments  0.597s

没捕获到异常是因为defer recover这种机制只是针对当前函数和以及直接调用的函数可能产生的panic,它无法处理其调用产生的其它协程的panic,这一点和try catch机制不一样

func zero_recover(t *testing.T){
	defer func() {
		err := recover() //获取异常
		if err != nil {
			t.Fatal("error: " + fmt.Sprintf("%s", err))
		}
	}()
	i :=0
	r := 5/i
	t.Logf("end zero %d!",r)
}
//协程异常处理
func TestRoutinePanicRecover(t *testing.T){
	t.Log("start test !")
	go  zero_recover(t)

	time.Sleep(3*time.Second)
}

输出结果:

D:\xzw\demo\go\异常捕获>go test -v -run TestRoutinePanicRecover recover_test.go
=== RUN   TestRoutinePanicRecover
--- FAIL: TestRoutinePanicRecover (3.00s)
    recover_test.go:119: start test !
    recover_test.go:110: error: runtime error: integer divide by zero
FAIL
FAIL    command-line-arguments  3.585s

这样就可以捕获到异常了

总结

  • go 里面panic错误可以recover ,go语言系统级别错误 fatal error 不能recover

  • defer recover这种机制只是针对当前函数和以及直接调用的函数可能产生的panic,它无法处理其调用产生的其它协程的panic,需要在其它协程里调用defer recover 才能解决

  • 函数发生异常抛出后,不会继续往下执行,会跳到recover里面,然后执行后面的其他函数

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×