做者:Stefan Nilssonhtml
原文网址:yourbasic.org/golang/reco…git
Panics 相似于 C++ 和 Java 异常,但仅适用于运行时错误,例如跟随一个 nil 指针或试图对数组访问超出范围的索引。为了表示诸如文件结束之类的事件,Go 程序使用内置 error
类型。有关错误的更多信息,请参见 错误处理最佳实践 和 3种建立错误的简单方法。github
Panic 中止 goroutine 的正常执行golang
panic 是由运行时错误或对内置函数 panic
的显式调用引发的。编程
堆栈跟踪记录 —— 全部活动堆栈帧的报告 —— 一般在 panic 发生时将其打印到控制台。堆栈跟踪对于调试很是有用:数组
这是一个堆栈跟踪的示例:bash
goroutine 11 [running]:
testing.tRunner.func1(0xc420092690)
/usr/local/go/src/testing/testing.go:711 +0x2d2
panic(0x53f820, 0x594da0)
/usr/local/go/src/runtime/panic.go:491 +0x283
github.com/yourbasic/bit.(*Set).Max(0xc42000a940, 0x0)
../src/github.com/bit/set_math_bits.go:137 +0x89
github.com/yourbasic/bit.TestMax(0xc420092690)
../src/github.com/bit/set_test.go:165 +0x337
testing.tRunner(0xc420092690, 0x57f5e8)
/usr/local/go/src/testing/testing.go:746 +0xd0
created by testing.(*T).Run
/usr/local/go/src/testing/testing.go:789 +0x2de
复制代码
能够从下至上阅读:数据结构
testing.(*T).Run
调用了 testing.tRunner
,testing.tRunner
调用了 bit.TestMax
,bit.TestMax
调用了 bit.(*Set).Max
,bit.(*Set).Max
调用了 panic
,panic
调用了 testing.tRunner.func1
缩进的行显示了调用该函数的源文件和行号。十六进制数字表示参数值,包括指针和内部数据结构的值。Go 中的堆栈跟踪 具备更多详细信息。框架
要打印当前 goroutine 的堆栈跟踪,请使用包 runtime/debug 中的debug.PrintStack。函数
您还能够经过调用 runtime.Stack 以编程方式检查当前的堆栈跟踪
变量 GOTRACEBACK 控制 Go 程序失败时生成的输出量。
GOTRACEBACK = none
彻底忽略 goroutine 堆栈跟踪。GOTRACEBACK = single
(默认)为当前goroutine打印堆栈跟踪, 从而消除运行时系统内部的功能。若是没有当前goroutine或故障是运行时内部的,则故障会打印全部goroutine的堆栈跟踪。GOTRACEBACK = all
为全部用户建立的goroutine添加堆栈跟踪。GOTRACEBACK = system
与其余系统同样,可是为运行时函数添加了堆栈框架,并显示了运行时在内部建立的 goroutine。内置的 recover 函数可用于从新得到对异常程序的控制并恢复正常执行。
由于展开时运行的惟一代码是在 defer 函数内部,因此 recover 仅在此类函数内部有用。
func main() {
n := foo()
fmt.Println("main received", n)
}
func foo() int {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
m := 1
panic("foo: fail")
m = 2
return m
}
复制代码
foo: fail
main received 0
复制代码
因为 panic 是在 foo 返回值以前发生的,所以 n 仍然具备其初始零值。
要在发生 panic 时返回值,必须使用命名返回值。
func main() {
n := foo()
fmt.Println("main received", n)
}
func foo() (m int) {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
m = 2
}
}()
m = 1
panic("foo: fail")
m = 3
return m
}
复制代码
foo: fail
main received 2
复制代码
在此示例中,咱们使用反射来检查接口变量列表是否具备与给定函数的参数相对应的类型。若是是这样,咱们使用这些参数调用该函数以检查是否有 panic。
// Panics tells if function f panics with parameters p.
func Panics(f interface{}, p ...interface{}) bool {
fv := reflect.ValueOf(f)
ft := reflect.TypeOf(f)
if ft.NumIn() != len(p) {
panic("wrong argument count")
}
pv := make([]reflect.Value, len(p))
for i, v := range p {
if reflect.TypeOf(v) != ft.In(i) {
panic("wrong argument type")
}
pv[i] = reflect.ValueOf(v)
}
return call(fv, pv)
}
func call(fv reflect.Value, pv []reflect.Value) (b bool) {
defer func() {
if err := recover(); err != nil {
b = true
}
}()
fv.Call(pv)
return
}
复制代码
扫描下方二维码,关注Feed
, 按期推送最新随笔