go recover和panic 组合使用-ag捕鱼王app官网

go recover和panic 组合使用

作者:迹忆客 最近更新:2023/01/08 浏览次数:

recover 是一个内置函数,用于重新获得对 panic 程序的控制。

recover 函数原型如下

func recover() interface{}  

仅当在延迟函数内部调用时,recover 才有用。 在延迟函数内执行恢复调用通过恢复正常执行来停止 panic 序列,并检索传递给 panic 函数调用的错误消息。 如果在延迟函数之外调用 recover,它不会停止panic 序列。

让我们修改一下我们的程序,使用 recover 来恢复 panic 后的正常执行。

package main
import (  
    "fmt"
)
func recoverfullname() {  
    if r := recover(); r!= nil {
        fmt.println("recovered from ", r)
    }
}
func fullname(firstname *string, lastname *string) {  
    defer recoverfullname()
    if firstname == nil {
        panic("runtime error: first name cannot be nil")
    }
    if lastname == nil {
        panic("runtime error: last name cannot be nil")
    }
    fmt.printf("%s %s\n", *firstname, *lastname)
    fmt.println("returned normally from fullname")
}
func main() {  
    defer fmt.println("deferred call in main")
    firstname := "elon"
    fullname(&firstname, nil)
    fmt.println("returned normally from main")
}

recoveryfullname() 函数调用recover() 返回传递给panic 函数调用的值。 这里我们只是打印了 recovery 返回的值。recoverfullname() 被推迟到fullname 函数。

当 fullname 发生紧急情况时,将调用延迟函数 recoveryname(),该函数使用 recovery() 来停止紧急情况序列。

上面程序输出如下

go 恢复panic程序

当程序发生 panic 时。延迟的 recoverfullname 函数被调用,该函数又调用 recover() 以重新获得对 panic 序列的控制。 对 recovery() 的调用返回传递给 panic() 的参数,因此它打印如下内容

recovered from  runtime error: last name cannot be nil 

执行 recover() 后,panic 停止,控制权返回给调用者,在这种情况下,是 main函数。 程序从 main 函数的 fmt.println("returned normally from main") 开始继续正常执行,因为 panic 已经恢复了😃。 它打印如下文本

returned normally from main

然后打印

deferred call in main

让我们再看一个例子,我们从访问切片的无效索引引起的panic中恢复正常。

package main
import (  
    "fmt"
)
func recoverinvalidaccess() {  
    if r := recover(); r != nil {
        fmt.println("recovered", r)
    }
}
func invalidsliceaccess() {  
    defer recoverinvalidaccess()
    n := []int{5, 7, 4}
    fmt.println(n[4])
    fmt.println("normally returned from a")
}
func main() {  
    invalidsliceaccess()
    fmt.println("normally returned from main")
}

上述程序输出如下

recovered runtime error: index out of range [4] with length 3  
normally returned from main  

从输出中,可以了解到我们已经从 panic 中恢复过来。

在 recover 恢复之后获取堆栈信息

如果我们从 panic 中恢复过来,我们就会丢失关于panic的堆栈跟踪。 在上述程序中,即使已经恢复了其实是丢失了堆栈跟踪的。

有一种方法,可以使用 debug 包的 printstack 函数打印堆栈跟踪

package main
import (  
    "fmt"
    "runtime/debug"
)
func recoverfullname() {  
    if r := recover(); r != nil {
        fmt.println("recovered from ", r)
        debug.printstack()
    }
}
func fullname(firstname *string, lastname *string) {  
    defer recoverfullname()
    if firstname == nil {
        panic("runtime error: first name cannot be nil")
    }
    if lastname == nil {
        panic("runtime error: last name cannot be nil")
    }
    fmt.printf("%s %s\n", *firstname, *lastname)
    fmt.println("returned normally from fullname")
}
func main() {  
    defer fmt.println("deferred call in main")
    firstname := "elon"
    fullname(&firstname, nil)
    fmt.println("returned normally from main")
}

在上面的程序中,我们使用 debug.printstack() 来打印堆栈信息。

上述程序执行结果如下

go 恢复panic打印堆栈信息

从上面的输出我们可以看到,panic被恢复了,并且打印如下信息

recovered from runtime error: last name cannot be nil

紧接着打印堆栈信息,然后打印如下结果

returned normally from main  
deferred call in main 

panic、recover 和 goroutines

recover 仅在从 panic 的同一个 goroutine 中调用时才起作用。 无法从不同 goroutine 中发生的 panic 中恢复过来。 让我们通过一个例子来理解这一点。

package main
import (  
    "fmt"
)
func recovery() {  
    if r := recover(); r != nil {
        fmt.println("recovered:", r)
    }
}
func sum(a int, b int) {  
    defer recovery()
    fmt.printf("%d   %d = %d\n", a, b, a b)
    done := make(chan bool)
    go divide(a, b, done)
    <-done
}
func divide(a int, b int, done chan bool) {  
    fmt.printf("%d / %d = %d", a, b, a/b)
    done <- true
}
func main() {  
    sum(5, 0)
    fmt.println("normally returned from main")
}

在上面的程序中,函数divide() 将会发生panic。 因为变量 b 是 nil 并且不可能将一个数除以零。 sum() 函数调用了一个延迟函数 recovery(),该函数用于从 panic 中恢复。 函数divide() 作为一个单独的goroutine 被调用。 我们在变量done的通道上进行等待。 确保divide() 完成执行。

你认为程序的输出是什么。 panic会恢复吗? 答案当然是不。 panic将无法恢复。 这是因为 recover 函数存在于不同的 goroutine 中,而panic发生在不同 goroutine 中的divide() 函数中。 因此是无法恢复的。

上述程序输出如下:

go 不同goroutine中的recover和panic

我们在输出中看到,recover并没有执行。

如果divide() 函数在同一个goroutine 中被调用,我们就会从 panic 中恢复过来。

如果将上面程序中的

go divide(a, b, done)

改为

divide(a, b, done)

recover将会执行,因为它和panic在同一个 goroutine 中。 如果程序按照上面进行更改,则打印结果如下

5   0 = 5  
recovered: runtime error: integer divide by zero  
normally returned from main  

到此我们就将 recover 介绍完成了。

本篇除了recover之外,还涉及到panic。推荐阅读go 语言中错误处理的 panic

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

c# 中的 goto 语句

发布时间:2024/02/02 浏览次数:184 分类:编程语言

本教程演示了如何在 c# 中使用 goto 以及何时使用它会有所帮助本教程将演示如何在 c# 中使用 goto 语法,并提供一些代码中的实际使用示例。

发布时间:2023/12/20 浏览次数:197 分类:python

本文为你提供了 python 中是否存在 goto 语句的答案。本文为你提供了 python 中是否存在 goto 语句的答案。基本上,python 不支持 goto 语句。

java goto

发布时间:2023/08/07 浏览次数:83 分类:java

与其他编程语言不同,java 没有 goto。 相反,java 包含关键字 label。关键字 label 的作用是改变程序的流程,根据指定的条件跳转到程序的另一段。

发布时间:2023/05/30 浏览次数:293 分类:python

当我们在 numpy 中传递一维数组而不是二维数组时,会发生错误 valueerror: expected 2d array, got 1d array instead 。如您所知,每种编程语言都会遇到很多错误,有些是在运行时,有些是在编译时。 pyth

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便
网站地图