专注app软件定制开发Go语言defer详解

1. 使用defer的优势

defer专注app软件定制开发一般用于资源的释放和专注app软件定制开发异常的捕捉, 专注app软件定制开发作为的特性之一.

defer 专注app软件定制开发语句会将其后面跟随的语句进行延迟处理. 意思就是说 跟在defer后面的语言 将会在程序进行最后的return之后再执行.

在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。

1.1 资源的释放

一般我们写读取文件的代码如下

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 	}	dst.Close()	src.Close()	return}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在程序最开始,os.Open及os.Create打开了两个文件资源描述符,并在最后通过file.Close方法得到释放,在正常情况下,该程序能正常运行,一旦在dstName文件创建过程中出现错误,程序就直接返回,src资源将得不到释放。因此需要在所有错误退出时释放资源,即修改为如下代码才能保证其在异常情况下的正确性。

即在每个err里面如果发生了异常, 要及时关闭src的资源.
这个问题出现在加锁中也非常常见

l.lock()// 如果下面发生了异常// 我们需要在每个err处理块中都加入l.unlock()来解锁// 不然资源就得不到释放, 就会产生死锁if err != nil {	l.unlock()	return}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

但是这样做未免太麻烦了, defer优雅的帮我们解决了这个问题
比如我们可以这样

	src, err := os.Open(srcName)	defer src.Close()	if err != nil {		return	}	dst, err := os.Create(dstName)	defer dst.Close()	if err != nil {		return 	}	------------------------------------------	l.lock()	defer l.unlock()	......	if err != nil {		return 	}	......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这样写的话, 就不需要在每个异常处理块中都加上Close() 或者 unlock()语句了

1.2 异常的捕捉

程序在运行时可能在任意的地方发生panic异常,例如算术除0错误、内存无效访问、数组越界等,这些错误会导致程序异常退出。在很多时候,我们希望能够捕获这样的错误,同时希望程序能够继续正常执行。一些语言采用try…catch语法,当try块中发生异常时,可以通过catch块捕获。

Go语言使用了特别的方式处理这一问题。defer的特性是无论后续函数的执行路径如何以及是否发生了panic,在函数结束后一定会得到执行,这为异常捕获提供了很好的时机。异常捕获通常结合recover函数一起使用。

如上所示,在executePanic函数中,手动执行panic函数触发了异常。当异常触发后,函数仍然会调用defer中的函数,然后异常退出。输出如下,表明调用了defer中的函数,并且main函数将不能正常运行,程序异常退出打印出栈追踪信息。

如下所示,当在defer函数中使用recover进行异常捕获后,程序将不会异常退出,并且能够执行正常的函数流程。如下输出表明,尽管有panic,main函数仍然在正常执行后退出。

使用了recover函数后, 程序将不会异常退出, 仍会正常执行

2. 多个defer语句的执行顺序

当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出), 相当于开辟了一个延时调用栈

func main() {    fmt.Println("defer begin")    // 将defer放入延迟调用栈    defer fmt.Println(1)    defer fmt.Println(2)    // 最后一个放入, 位于栈顶, 最先调用    defer fmt.Println(3)    fmt.Println("defer end")}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

执行的结果就是

// 先打印正常语句defer begindefer end// 然后按从上到下的顺序执行defer调用栈中的语句321
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发