不同协程的异常捕获问题?

func main() {
    GoA()
    time.Sleep(1 * time.Second)
    fmt.Println("main")
}

func GoA() {
    defer (func(){
        if err := recover(); err != nil {
            fmt.Println("panic:" + fmt.Sprintf("%s", err))
        }
    })()

    go GoB()
}

func GoB() {
    panic("error")
}

因为 defer 只对当前协程有效, panic("error") 无法捕获?

有什么好的方法,可以捕获到吗?

阅读 7k
6 个回答

我觉得是你写的有问题。。。
这个写法,你根本不能保证先执行panic还是先执行defer

谢邀!

千言万语不如看几个实例

实例1,在线源码调试:

package main

import (
    "fmt"
    "time"
)

func catchErr(num int) {
    // Try removing the following code of defer block
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("[recover]", err, num)
        }
    }()

    fmt.Println("goroutine", num)
    panic("panic occurred ...")
}

func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("main recover: ", err)
        }
    }()

    for i := 0; i < 3; i++ {
        fmt.Println("main goroutine", i)
        go catchErr(i)
        time.Sleep(time.Second * 1)
    }

start:
    goto start

}

实例2,在线源码调试:

// Go program which illustrates 
// recover in a goroutine 
package main 

import ( 
    "fmt"
    "time"
) 

// For recovery 
func handlepanic() { 
    if a := recover(); a != nil { 
        fmt.Println("RECOVER", a) 
    } 
} 

/* Here, this panic is not 
handled by the recover 
function because of the 
recover function is not 
called in the same 
goroutine in which the 
panic occurs */

// Function 1 
func myfun1() { 

    defer handlepanic() 
    fmt.Println("Welcome to Function 1") 
    go myfun2() 
    time.Sleep(10 * time.Second) 
} 

// Function 2 
func myfun2() { 

    fmt.Println("Welcome to Function 2") 
    panic("Panicked!!") 
} 

// Main function 
func main() { 

    myfun1() 
    fmt.Println("Return successfully from the main function") 
} 

总结

  1. recover() 必须写在defer语块中才能生效
  2. recover() 的作用范围仅限于当前的所属go routine
  3. 既然你要使用panic,那为什么要recover?你的期望是什么?如果不希望go die 为什么要用panic?
  4. 如上面的答案所说,如果实在每个panic都想捕获,那就把panic这样的事件通知给其他goroutine处理。

Handling Panics:

The recover function allows a program to manage behavior of a panicking goroutine. Suppose a function G defers a function D that calls recover and a panic occurs in a function on the same goroutine in which G is executing. When the running of deferred functions reaches D, the return value of D's call to recover will be the value passed to the call of panic. If D returns normally, without starting a new panic, the panicking sequence stops. In that case, the state of functions called between G and the call to panic is discarded, and normal execution resumes. Any functions deferred by G before D are then run and G's execution terminates by returning to its caller.

recover 只能处理当前 goroutine 的 panic

如上面同学所说,recover只能捕获当前goroutine的panic,但如果在业务中,一般如果我们想捕获panic,往往是为了做一些异常补救工作,我们可以把出现panic这样的事件通知给其他goroutine去处理

这个问题很简单。
在开启协程之前,先调用 c := make(chan struct{})创建一个channel,然后把c传入到协程运行的函数中,当协程内函数出现panic时,c <- struct{}{},往c中塞入消息。
在外部通过for循环和select ... case 来监听c,根据c中的内容来判断发生了什么状态导致出现了panic,以及做出相应修改。

新手上路,请多包涵

其他答案都是一坨屎,你们会不会写代码。专门注册个账号来写一下答案:

func main() {
    GoA()
    time.Sleep(1 * time.Second)
    fmt.Println("main")
}

func GoA() {
    go func() {
        defer func() {
            if err := recover(); err != nil {
                fmt.Println("panic:" + fmt.Sprintf("%s", err))
            }
        }()
        GoB()
    }()
}

func GoB() {
    panic("error")
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题