zoukankan      html  css  js  c++  java
  • golang recover rgb

    使用

    在 Golang 中可以通过 recover 来捕获 panic 恢复程序,使用时需要注意:

    1. recover 只能捕获 当前协程 的 panic,类似下面的方式捕获会失败:

      package main
      
      import (
        "log"
      )
      
      func main() {
        // Recover outside a goroutine
        defer func() {
          log.Println(recover())
        }()
      
        go func() {
          // Panic occurs in a goroutine
          panic("A bad boy stole a server")
        }()
      }
      
    2. recover 只在 deferred 函数中有效,间接 defer 调用也不会成功:

      package main
      
      import (
        "fmt"
        "log"
        "time"
      )
      
      func getItem() {
        go func() {
          // Call Recover() Tool Function.
          defer func() {
            Recover("Panic in goroutine")
          }()
          // Panic here.
          panic("A bad boy stole the server")
          // Test the panic result.
          fmt.Println("Will NOT Reach Here")
        }()
      }
      
      func Recover(funcName string) {
        if err := recover(); err != nil {
          // If the panic is catched.
          log.Printf("panic para: %v, panic info: %v\n", funcName, err)
        }
      }
      
      func main() {
        getItem() // Fail to catch the panic, program will crash
        time.Sleep(time.Second)
      }
      

    官方文档中的描述:

    The return value of recover is nil if any of the following conditions holds:

    • panic's argument was nil;
    • the goroutine is not panicking;
    • recover was not called directly by a deferred function.

    当 pnaic 参数为 nil 时,虽然 recover 返回值也是 nil,但当前 panic 仍然会被被标记为 recovered,并恢复程序的执行。

    原理

    在 Golang 中程序运行的基本单位为 Goroutine,在 runtime 中每个 Goroutine 都对应一个结构体 g,在结构体 g 上存在有 panic 和 defer 链表:

    type g struct {
      _panic *_panic
      _defer *_deder
    }
    

    当代码中出现 panic 会,会创建一个 _panic 结构绑定到当前 Goroutine 上并遍历当前 Goroutine 上绑定的 _defer 链表执行:

    func gopanic(e interface{}) {
      gp := getg()
    
      var p _panic
      p.arg = e
      p.link = gp._panic
      gp._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
    
      for {
        d := gp._defer
        if d == nil {
          break
        }
    
        d._panic = (*_panic)(noescape(unsafe.Pointer(&p)))
    
        p.argp = unsafe.Pointer(getargp(0))  // 指向当前 defer 函数的参数列表指针
        reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
        p.argp = nil
    
        d._panic = nil
        d.fn = nil
        gp._defer = d.link
    
        freedefer(d)
        if p.recovered {
          //...
        }
      }
    
      fatalpanic(gp._panic)
      *(*int)(nil) = 0
    }
    

    而在执行 defer 中的 recover 时,会判断当前 _panic 上的 argp 和当前 argp 是否相同:

    func gorecover(argp uintptr) interface{} {
      // Must be in a function running as part of a deferred call during the panic.
      // Must be called from the topmost function of the call
      // (the function used in the defer statement).
      // p.argp is the argument pointer of that topmost deferred function call.
      // Compare against argp reported by caller.
      // If they match, the caller is the one who can recover.
      gp := getg()
      p := gp._panic
      if p != nil && !p.goexit && !p.recovered && argp == uintptr(p.argp) {
        p.recovered = true
        return p.arg
      }
      return nil
    }
    

    因此,如果 recover 不在当前 Goroutine 的 deferred 函数中,那么就不会执行 p.recovered = true 语句, 而 runtime.gopanic 函数依赖于 _panic.recovered 的值来判断是否恢复代码的执行。

  • 相关阅读:
    排名第一、第二的OCR软件
    补码输出
    枚举 与 枚举的应用
    动态构造结构体数组
    c 冒泡排序
    strcpy
    typedef用法
    C 结构体小结
    int 占一个机器字长
    SQL Server创建视图——视图的作用
  • 原文地址:https://www.cnblogs.com/rgbit/p/15612445.html
Copyright © 2011-2022 走看看