Golang异常处理机制

发布于:2024-07-11 ⋅ 阅读:(23) ⋅ 点赞:(0)

 go语言使用error来处理错误,用panic和recover来处理异常

error

go语言的错误处理有两个发展阶段,以go1.13版本为分水岭,在1.13版本之前,标准库对error的支持非常有限,仅有errors.New()和fmt.Errorf()两个函数来构造errorString实例。在这个阶段,error处理存在一个非常大的问题:传递错误时,使用fmt.Errorf()传递捕获的error并为error增加上下文信息时,原error将和上下文的描述信息混杂在一起,导致原始的error信息丢失。

在go1.13版本,Go官方加强了对errors的支持,在进行错误传递的时候,采用链式传递的方式,将同一上下文的error链接起来,并且保留原始的error信息。通过在fmt.Errorf()中增加一个格式动词%w,来创建链式的error,并且提供errors.Unwarp()来拆解error。

创建错误的两种方式:

  1. errors.New(str string)

  2. fmt.Errorf("format string",a ...any)

我们可以通过以上两种方式来创建错误类型,第一种创建出来的是stringError,第二种方式创建出来的类型,取决于传入的格式动词,在1.13版本之前只能够创建stringError类型的错误。在1.13版本之后,通过传入格式动词%w,可以创建出warpError类型的错误,我们需要注意的是,传入的%w应该和传入的err来配对。

defer

不仅函数正常返回会执行被defer延迟的函数,函数中任意一个return语句、panic语句均会触发延迟函数。

defer的三大规则:

  1. 延迟函数的参数在defer语句出现时就已经确定了,延迟函数体中引用的变量,在编译的会绑定主函数中的对应变量,会获取变量的最终值。【对于指针函数的变量,复制的是地址值】

  2. 延迟函数按后进先出的顺序执行,即先出现的defer最后执行

  3. 延迟函数可能会操作主函数的具名返回值。【return的返回操作并不是原子性的,而是分为两个步骤:一是设置返回值,二是执行跳转,defer语句发生在跳转之前】(分析方向,先判断返回值的类型,是匿名还是具名,只有在defer语句中,对具名返回值进行更改的时候,才会修改返回值)

panic

panic的工作机制:每一个协程中都维护一个defer链表,执行过程中每遇到一个defer语句都会创建一个defer实例,并将这一个defer实例插入链表,函数退出时取出本函数创建的defer实例并执行。panic发生时,实际上是把程序的流程转向了这一个defer链表,程序专注于消费链表中的defer函数,当链表中的defer函数被消费完,再触发程序的退出。

我们在程序中一些可以预料到的、业务上的错误,可以通过定义err进行异常处理,处理err时,我们的程序是正常的,按照我们编写的逻辑顺序执行的。 而panic则用来处理不可预料到的错误和进行危险操作时触发panic。一旦程序发生panic,那么panic所在的函数体之后的代码将不会再执行,转而递归执行当前协程所有的defer函数,当前协程中的所有defer处理完成后,触发程序退出。如果panic执行过程中任意一个defer函数执行了recover(),那么panic的处理流程就会中止。程序继续执行上游函数中的代码。

recover

内置函数recover()用于消除panic并使程序回到正常的执行流程中。

recover的返回值是捕获到的panic()函数的参数

recover()执行之后,程序的执行流程转向上游函数,并且上游函数感知不到panic的发生。而发生panic的函数后面的代码不会被执行。【一旦发生惊慌,函数的执行流程就变了】

recover()函数必须直接位于defer函数中才有效。defer func() { recover() }()