go 并发环境下的错误捕获与处理

3,839 阅读2分钟

协程A的panic,协程B能捕获吗

不能,协程需要自己在内部捕获自己的panic,其他协程无法捕获

func main() {
   go func() { // goroutine协程
      defer func() {
         if e := recover();e != nil { // 捕获该协程的panic 111111
            fmt.Println("recover ",e)
         }
      }()
      panic("0000000")
      fmt.Println("11111111") // 无法打印,panic之后就终止了不再执行后续的逻辑
   }()

   defer func() {
      if e := recover();e != nil { // 捕获main协程的panic 222222
         fmt.Println("recover ",e)
      }
   }()
   panic("222222 ") // mian 协程的panic
   fmt.Println("33333") // panic之后就终止了不再执行后续的逻辑
   time.Sleep(2*time.Second) // 保证运行gofunc协程
}

为什么不能捕获:
因为panic和recover是在G结构里边的,自然无法捕获其他协程的panic

一个协程内的recover能捕获多次panic吗

不能,一个recover只能捕获一次panic,一一对应

func main() {
   go func() { // goroutine协程
      defer func() {
         if e := recover();e != nil { // 捕获该协程的panic 111111
            fmt.Println("recover ",e)
         }
      }()
      panic("0000000")
      panic("44444444") // 该panic无法捕获
      fmt.Println("11111111") // 无法打印,panic之后就终止了不再执行后续的逻辑
   }()
}

如何合理的使用协程,recover ,panic

可以在项目中把使用协程的逻辑封装成函数,统一管理入口处理

func callGoroutine(handlers ...func() error) (err error) {
   var wg sync.WaitGroup
   for _, f := range handlers {
      wg.Add(1)
      // 每个函数启动一个协程
      go func(handler func() error) {
         defer func() {
            // 每个协程内部使用recover捕获可能在调用逻辑中发生的panic
            if e := recover(); e != nil {
               // 日志记录失败信息,捕获panic,不影响其他协程跟主协程运行
               fmt.Println("recover ",e)
            }
            defer wg.Done()
         }()
         // 取第一个报错的handler调用逻辑,并最终向外返回
         e := handler()
         if err == nil && e != nil {
            err = e
         }
      }(f)
   }

   wg.Wait()

   return
}

func main() {
   userRpc := func() error {
      panic("userRpc fail ")
      return nil
   }

   // 调用逻辑2
   orderRpc := func() error {
      panic("orderRpc fail")
      return nil
   }

   err := callGoroutine(userRpc,orderRpc)
   if err != nil {
      fmt.Println(err)
   }
}