// a custom dangerous go routine, 10s later app will crash!!!! //go func() { // time.Sleep(time.Second * 10) // panic("dangerous") //}() // use above code instead! GoSafe(context.Background(), func(ctx context.Context) { time.Sleep(time.Second * 10) panic("dangerous") })
第2个问题,是由于painc机制,如果没有被处理,它会调用 os.Exit(2) 退出进程,所以算是进程主动退出,故操作系统不会发送kill信号,也就无法进入优雅退出机制。
下面是部分源码(runtime/panic.go):
func gopanic(e interface{}) { gp := getg() var p _panic p.arg = e p.link = gp._panic //p指向更早的panic gp._panic = (*_panic)(noescape(unsafe.Pointer(&p))) atomic.Xadd(&runningPanicDefers, 1) //遍历defer链表 for { d := gp._defer if d == nil { break } // 如果defer已经启动,跳过 if d.started { gp._defer = d.link freedefer(d) //释放defer continue } // 标识defer已经启动 d.started = true // 记录是当前Panic运行这个defer。如果在defer运行期间,有新的Panic,将会标记这个Panic abort=true(强制终止) d._panic = (*_panic)(noescape(unsafe.Pointer(&p))) p.argp = unsafe.Pointer(getargp(0)) // 调用 defer reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz)) p.argp = nil // reflectcall did not panic. Remove d. if gp._defer != d { throw("bad defer entry in panic") } d._panic = nil d.fn = nil gp._defer = d.link //遍历到下一个defer pc := d.pc sp := unsafe.Pointer(d.sp) // must be pointer so it gets adjusted during stack copy freedefer(d) //已经有recover被调用 if p.recovered { //调用recovery函数 mcall(recovery) throw("recovery failed") // mcall should not return } } //defer遍历完,终止程序 fatalpanic(gp._panic) // should not return *(*int)(nil) = 0 // not reached } //panic没有被recover,会运行fatalpanic func fatalpanic(msgs *_panic) { systemstack(func() { if startpanic_m() && msgs != nil { //打印panic messages printpanics(msgs) } //打印panic messages docrash = dopanic_m(gp, pc, sp) }) //终止整个程序,所以需要注意:如果goroutine的Panic没有 recover,会终止整个程序 systemstack(func() { exit(2) }) *(*int)(nil) = 0 // not reached }
我们可以确定,当panic没有被处理时,runtime 会调用 exit(2) 退出整个应用程序!