聊聊Go语言的异常处理机制

背景

最近因为遇到了一个panic问题,加上之前零零散散看了些关于程序异常处理相关的东西,对这块有点兴趣,于是整理了一下golang对于异常处理的机制。

名词介绍

Painc

golang的内置方法,能够改变程序的控制流。 当函数调用了panic,函数会停止运行,但是defer函数会运行,程序会在当前panic的goroutine全部退栈以后crash。

Recover

recover也是golang的内置方法,用于恢复发生panic的goroutine的控制,recover只在defer函数中生效。如果当前goroutine将要发生panic的话, recover会捕获这个panic,并恢复正常执行。

Defer

聊到panic和recover,需要聊聊defer这个关键字,后面会看到defer在异常处理机制中起到的作用。go的defer是用来延迟执行函数的,延迟的发生是在调用函数的returen之后。

相关问题

在聊Golang异常机制之前,我们可以先问几个问题,带着这些问题看看后续怎么解答。

  1. recover的代码为什么只能在defer里面执行。
  2. panic自己主动可以触发,系统遇到例如除0的请求也会触发panic,那这两种panic在处理上有什么区别。
  3. recover和defer方法中,如果再发生了panic,Golang是如何处理的
  4. Golang中关于运行的代码中是如何感知系统异常

使用场景

package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}

比如这个例子,看下这里关于panic、recover和defer的一个基本用法。f函数中通过defer函数,包了一层recover的执行。

执行以后的输出:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

前面输出比较正常就不多说,到Panicking!输出以后,我们看到在g函数内部的defer函数先执行,先defer的语句会后输出,所以是 defer in g 3 2 1 0的顺序。 执行完以后回到f函数,f函数的recover函数拿到当前panic传入的参数4,并且输出。完成defer函数的输出以后,输出函数回到main继续执行。

如果我们把上面f函数的defer function去除,那么panic的话就不会被recovered到,并且输出如下:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
panic: 4

panic PC=0x2a9cd8
[stack trace omitted]

源码实现

上面的例子,我们看到了panic、recover、 defer的一个基本使用。 当然这些并不能解决我们之前的几个疑问,我们有必要深入源码角度看看Golang是如何实现这套异常处理流程的,并且回答我们之前提出的一些问题。

Go语言层实现

目前手上的代码库是Go SDK 1.13.4,GO sdk的代码目录也比较清晰,在runtime目录下可以找到panic.go这个文件。

聊聊Go语言的异常处理机制_第1张图片

_panic结构和defer结构

在panic函数具体了解之前,有必要先看一下后面会出现的两种数据结构,一个是_panic结构,一个是_defer结构

_panic结构

聊聊Go语言的异常处理机制_第2张图片

_panic结构主体包括下面这些参数:

argp: 指向defer调用时参数的指针

a

你可能感兴趣的:(深入理解Go语言,go)