处理 go 例程中的恐慌

Handling panics in go routines

我了解使用处理紧急恢复。但是在go routine

出现panic时,下面的block无法恢复
func main() {
    done := make(chan int64)
    defer fmt.Println("Graceful End of program")
    defer func() {
     r := recover()
     if _, ok := r.(error); ok {
        fmt.Println("Recovered")
     }
    }()

    go handle(done)
    for {
        select{
        case <- done:
        return
        }
    } 
}

func handle(done chan int64) {
    var a *int64
    a = nil

    fmt.Println(*a)
    done <- *a
}

但是下面的块能够按预期执行

func main() {
    done := make(chan int64)
    defer fmt.Println("Graceful End of program")
    defer func() {
     r := recover()
     if _, ok := r.(error); ok {
        fmt.Println("Recovered")
     }
    }()

    handle(done)
    for {
        select{
        case <- done:
        return
        }
    } 
}

func handle(done chan int64) {
    var a *int64
    a = nil

    fmt.Println(*a)
    done <- *a
}

如何从 go 例程中出现的恐慌中恢复。这是 playground 的 link:https://play.golang.org/p/lkvKUxMHjhi

仅当从调用 panic 的同一个 goroutine 调用时,恢复才有效。来自 Go 博客:

The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes

您必须在 goroutine 中进行延迟恢复。

https://blog.golang.org/defer-panic-and-recover

文档/规范也包含相同的内容:

While executing a function F, an explicit call to panic or a run-time panic terminates the execution of F. Any functions deferred by F are then executed as usual. Next, any deferred functions run by F's caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking

https://golang.org/ref/spec#Handling_panics

我用下面的方法来处理这种情况,效果和预期的一样。

package main

import (
    "fmt"
    "time"
)

func main() {
    defer fmt.Println("Graceful End of program")
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered:", r)
        }
    }()

    done := make(chan int64)
    var panicVar interface{}

    go handle(done, &panicVar)

WAIT:
    for {
        select {
        case <-done:
            break WAIT // break WAIT: goroutine exit normally
        default:
            if panicVar != nil {
                break WAIT // break WAIT: goroutine exit panicked
            }

            // wait for goroutine exit
        }

        time.Sleep(1 * time.Microsecond)
    }

    if panicVar != nil {
        panic(panicVar) // panic again
    }
}

func handle(done chan int64, panicVar *interface{}) {
    defer func() {
        if r := recover(); r != nil {
            // pass panic variable outside
            *panicVar = r
        }
    }()

    var a *int64
    a = nil

    fmt.Println(*a)
    done <- *a
}

游乐场link:https://play.golang.org/p/t0wXwB02pa3