golang中Defer、Panic、Recover的用法

xiaoxiao2022-06-12  34

因为golang没有try……catch的用法,但是可以通过defer + recover来实现,但是要先明确defer与return之间的执行顺序。

defer

先看defer的定义(参考tour go)。

defer关键字可以推迟它所修饰的语句的执行,知道被它包围的代码执行完成后,才执行,一般用于执行一些清理工作(资源释放、关闭连接等等)来简化代码。可以看下面一段代码,代码打开两个文件并把其中一个文件的内容拷贝到另一个文件中,

func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } dst, err := os.Create(dstName) if err != nil { return } written, err = io.Copy(dst, src) dst.Close() src.Close() return }

这段代码可以奏效,但是存在bug。如果调用os.Create失败了,函数还没有释放资源就返回了。如果函数更复杂,潜在的bug就会更多了。通过defer我们可以确保文件资源都被释放。

func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src.Close() dst, err := os.Create(dstName) if err != nil { return } defer dst.Close() return io.Copy(dst, src) }

defer语句简单有效,简单来看,就三个规则:

1:defer修饰函数的变量在defer被声明时就确定了,例如下面的代码

func a() { i := 0 defer fmt.Println(i) i++ return }

代码最终会输出 0 而不是 1 。这是因为在声明defer修饰的 fmt.Println(i)时,变量i的值就确定了,这时i的值还是 0 。

2:多个defer的定义与执行类似于栈的操作:先进后出,最先定义的最后执行。

package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") }

这段代码的执行结果:

counting done 9 8 7 6 5 4 3 2 1 0

3:defer修饰的函数可以读取并且赋值给函数的返回值(这里有点翻译的不通顺,先看代码吧)

package main import "fmt" func main() { fmt.Println(c()) } func c() (i int) { defer func() { i++ }() return 1 }

代码最终输出结果是 2 。defer修饰的函数可以读取返回的 i 值并且给他赋新值。

Panic

Panic是一个内置函数,可以打断正常流程。例如一个函数F调用了panic,F停止执行,F所有的defer函数会正常执行,defer函数都执行完后,F函数返回给它的调用者。对于F函数的调用者来说,此时,F函数的返回相当于又调用了一次panic。F函数的调用者就像F函数一样执行相同的逻辑,直到这个goroutine栈里所有的函数都panic返回了,进而整个程序终止了(如果goroutine没有对panic通过defer进行捕捉)。

Panic可以调用panic函数直接触发,也会因为一些其他runtime error触发,例如数组越界等等。

Recover

Recover也是一个内置函数,可以重新获取一个panic的goroutine的控制权。Recover只有和defer结合起来,内置在defer函数中,才会起作用。如果函数正常执行,调用recover()返回的是nil,如果当前goroutine处于panic,调用recover()就可以捕捉到panic时设定的value,然后继续正常的执行流程。

下面是panic和defer机制的一个示例代码,

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) }

代码输出:

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.

未完待续……

转载请注明原文地址: https://www.6miu.com/read-4932759.html

最新回复(0)