【问题标题】:Handling panics in go routines处理 go 例程中的恐慌
【发布时间】:2018-10-28 18:12:44
【问题描述】:

我知道使用了处理恐慌恢复。但是当 goroutine 中出现恐慌时,以下块无法恢复

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 例程中出现的恐慌中恢复。这是游乐场的链接:https://play.golang.org/p/lkvKUxMHjhi

【问题讨论】:

    标签: go recovery panic


    【解决方案1】:

    Recover 仅在调用恐慌时从同一个 goroutine 调用时才有效。来自 Go 博客:

    进程继续向上堆栈,直到当前的所有函数 goroutine 已经返回,此时程序崩溃

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

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

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

    在执行函数 F 时,显式调用 panic 或运行时 恐慌终止 F 的执行。任何由 F 延迟的函数都是 然后照常执行。接下来,由 F 的调用者运行的任何延迟函数 运行,依此类推,直到任何由顶级函数延迟 执行 goroutine。此时,程序终止,并且 报告错误情况,包括参数的值 恐慌。这种终止序列称为恐慌

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

    【讨论】:

      【解决方案2】:

      我使用下面的方式来处理这种情况,它按预期工作。

      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
      }
      

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

      【讨论】:

      • 不要使用 var 来存储恐慌状态,而是使用通道。这样你就可以在 main 中移除 sleep & for。在这种情况下,单独选择可以投放。
      猜你喜欢
      • 2022-01-25
      • 2016-02-27
      • 2019-10-07
      • 1970-01-01
      • 2021-07-23
      • 1970-01-01
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      相关资源
      最近更新 更多