// 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) 退出整个应用程序!