本文为 工作用Go: 异步任务怎么写 系列的第3篇
在继续讲解之前, 一定要提一下使用 go
开协程的一个坑, 或者说一个非常重要的基础知识:
Go基础知识: panic只对当前goroutine的defer有效
Go中出现 panic()
, 程序会立即终止:
funcTestPanic(t*testing.T) { panic("panic") log.Print("end") }
=== RUN TestPanic --- FAIL: TestPanic (0.00s) panic: panic [recovered] panic: panic goroutine 118 [running]: testing.tRunner.func1.2({0x103e15940, 0x10405c208}) /opt/homebrew/opt/go/libexec/src/testing/testing.go:1396 +0x1c8 testing.tRunner.func1() /opt/homebrew/opt/go/libexec/src/testing/testing.go:1399 +0x378 panic({0x103e15940, 0x10405c208}) /opt/homebrew/opt/go/libexec/src/runtime/panic.go:884 +0x204 pkg.poizon.com/ee/ehr/tests/util.TestPanic(0x0?) /Users/dayday/dw/ehr/tests/util/pkg_test.go:34 +0x2c testing.tRunner(0x14000603040, 0x104058678) /opt/homebrew/opt/go/libexec/src/testing/testing.go:1446 +0x10c created by testing.(*T).Run /opt/homebrew/opt/go/libexec/src/testing/testing.go:1493 +0x300 Process finished with the exit code 1
- 可以看到,
panic
后程序直接退出,panic
后的log.Print("end")
并没有执行
当然, 想要程序健壮一些, panic
是可以 吃掉
的:
funcTestPanic(t*testing.T) { deferfunc() { ifr :=recover(); r!=nil { log.Print(r) } }() panic("panic") log.Print("end") }
=== RUN TestPanic 2022/11/17 22:25:08 panic --- PASS: TestPanic (0.00s) PASS
使用 recover()
对 panic()
进行恢复, 程序就不会崩掉(exit)
但是, 一定要注意
panic只对当前goroutine的defer有效!
panic只对当前goroutine的defer有效!
panic只对当前goroutine的defer有效!
重要的事情说三遍.
funcTestPanic(t*testing.T) { deferfunc() { ifr :=recover(); r!=nil { log.Print(r) } }() gofunc() { panic("panic") }() log.Print("end") }
===RUNTestPanicpanic: panicgoroutine88 [running]: pkg.poizon.com/ee/ehr/tests/util.TestPanic.func2() /Users/dayday/dw/ehr/tests/util/pkg_test.go:41+0x2ccreatedbypkg.poizon.com/ee/ehr/tests/util.TestPanic/Users/dayday/dw/ehr/tests/util/pkg_test.go:40+0x40Processfinishedwiththeexitcode1
而 go 里面开协程又是如此的方便, 简单一个 go
关键字即可, 所以大家给这种情况起了个外号: 野生 Goroutine. 最简单的做法就是对协程进行一次封装, 比如这样:
packagegox// Run start with a goroutinefuncRun(fnfunc()) { gofunc() { deferfunc() { ifr :=recover(); r!=nil { log.Print(r) } }() fn() }() }
原本的 go task()
, 使用 gox.Run(task)
进行替换, 就可以 task 出现 panic 的时候, 程序还能恢复