今天刚好有人问这个问题,就记录一下(讲了老半天说了这么多,一句‘嗯好的’就把我打发了·~~~~~,一点都不带客气的)
先看第一段代码
func main() {
defer func() {
if err := recover(); err != nil {
log.Println("err:", err)
}
}()
go out()
time.Sleep(time.Second * 3)
panic(errors.New("stop test"))
log.Println("123")
select {}
}
func out() {
for {
tm := time.NewTicker(time.Second)
select {
case <-tm.C:
log.Println("out")
}
}
}
输出:
2021/02/05 10:27:42 out
2021/02/05 10:27:43 err: stop test
解释:
这里输出一次就停止了,说明不管main还是协程都停了,虽然这里处理的panic,但是直接导致了整个程序的奔溃。
使用了defer只是做了panic的处理而已,不能使程序继续运行。那该怎么做呢,看第二段
func main() {
defer func() {
if err := recover(); err != nil {
log.Println("err:", err)
}
}()
go out()
time.Sleep(time.Second * 3)
load()
log.Println("123")
select {}
}
func out() {
for {
tm := time.NewTicker(time.Second)
select {
case <-tm.C:
log.Println("out")
}
}
}
func load() {
defer func() {
if err := recover(); err != nil {
log.Println("load err:", err)
}
}()
panic(errors.New("stop test"))
}
对应输出:
2021/02/05 10:32:16 out
2021/02/05 10:32:17 out
2021/02/05 10:32:18 load err: stop test
2021/02/05 10:32:18 123
2021/02/05 10:32:18 out
.
.
.
2021/02/05 10:32:19 out
2021/02/05 10:32:20 out
2021/02/05 10:32:21 out
2021/02/05 10:32:22 out
解释:
这里没有导致全部的程序奔溃,而是在load中处理了这个panic,所以main中不会接收到这个panic,则out协程也不会退出。
技术点详解:
1.panic的机制
panic会将这个异常不断向上抛出(类似函数调用方向相反的情形),直到有地方处理它。所以上述在子函数中处理了这个panic,就不会向上抛出到main中,则保证main中的协程一直处理。
原文链接:https://blog.golang.org/defer-panic-and-recover
原文解释:
Panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller. To the caller, F then behaves like a call to panic. The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes. Panics can be initiated by invoking panic directly. They can also be caused by runtime errors, such as out-of-bounds array accesses.
Panic是一个内置函数,可停止常规控制流并开始恐慌。当函数F调用恐慌时,F的执行停止,F中任何延迟的函数都将正常执行,然后F返回其调用方。对于呼叫者,F然后表现得像是发生了恐慌。该过程将继续执行堆栈,直到返回当前goroutine中的所有函数为止,此时程序崩溃。紧急事件可以通过直接调用紧急事件来启动。它们也可能是由运行时错误引起的,例如越界数组访问。
Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panicbai'du and resume normal execution.
恢复是一个内置函数,可以重新获得对紧急恐慌例程的控制。恢复仅在延迟函数内部有用。在正常执行期间,恢复调用将返回nil并且没有其他效果。如果当前goroutine处于恐慌状态,则调用restore会捕获提供给panic的值并恢复正常执行。
提问:
如果这个协程是在load中开启的会怎么样呢,还会继续处理吗
例如如下代码:
func main() {
defer func() {
if err := recover(); err != nil {
log.Println("err:", err)
}
}()
time.Sleep(time.Second * 3)
load()
log.Println("123")
select {}
}
func out() {
for {
tm := time.NewTicker(time.Second)
select {
case <-tm.C:
log.Println("out")
}
}
}
func load() {
defer func() {
if err := recover(); err != nil {
log.Println("load err:", err)
}
}()
go out()
panic(errors.New("stop test"))
}
答案是肯定会继续执行协程中的逻辑啦:因为即使是这个函数退出了,新开的协程是相当于基于main下的一个子程,只要main不退出,他依然会“存活”
有疑问加站长微信联系(非本文作者)