Go defer 特性和使用场景

wangshizebin · · 1552 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

golang 的 defer 语句用于延迟调用。defer 会在当前函数返回之前执行 defer 注册的函数。比如 defer func_defer() 这样语句会让你注册一个函数变量到 defer 的全局链表中,在 defer 语句所在的函数退出之前调用。 defer 可以代替其它语言中 try…catch… 语句,也可以用来处理释放资源等收尾操作,比如关闭文件句柄、关闭数据库连接等。defer 还能用于 panic 的 recovery。 ## 1. defer 的特性 我们先深入的剖析下 defer 具有的特性,知其然也。这些特性是需要我们记住的特点,才能更好的理解 defer 使用的场景。 ### 1) 延迟调用 ``` package main func main() { defer println("--- defer ---") println("--- end ---") } ``` 运行结果: ``` --- end --- --- defer --- ``` defer 会在 main 函数所有语句之后, return 之前时候调用。核心要点: 延迟调用:defer 语句本身虽然是 main 的第一行,但是 println("--- end ---") 先打印的; defer 关键字一定是处于函数上下文:defer 必须放在函数内部; ### 2) LIFO 一个函数中含有有多个 defer,调用顺序采用压栈式执行,后入先出(LIFO)。 ``` package main import ( "strconv" ) func main() { for i := 1; i <= 3; i++ { defer println("defer -->" + strconv.Itoa(i)) } println("--- end ---") } ``` 压栈式执行,也就是说先注册的函数后调用。如上,我们注册的顺序式 1,2,3,最后打印 “--- end ---”,所以执行的结果自然是反着来的,程序输出: ``` --- end --- defer -->3 defer -->2 defer -->1 ``` ### 3) 作用域 defer 只会和 defer 语句所在的特定函数绑定在一起,作用域也只在这个函数。 从语法上来讲,defer 语句也一定要在函数内,否则会报告语法错误。 ``` package main func main() { func() { defer println("--- defer ---") }() println("--- end ---") } ``` 如上,defer 处于一个匿名函数中,就 main 函数本身来讲,匿名函数 fun(){}() 先调用且返回,然后再调用 println("--- end ---") ,所以程序输出自然是: ``` --- defer --- --- end --- ``` ## 4) 异常场景 这个是非常重要的特性:panic 也能执行。golang 不鼓励异常的编程模式,但是却也留了 panic-recover 这个异常和捕捉异常的机制。所以 defer 机制就显得尤为重要,甚至可以说是必不可少的。因为你没有一个无视异常,永保调用的 defer 机制,很有可能就会发生各种资源泄露,死锁等场景。为什么?因为发生了 panic 却不代表进程一定会挂掉,很有可能被外层 recover 住。 ``` package main func main() { defer func() { if e := recover(); e != nil { println("--- defer ---") } }() panic("throw panic") } ``` 如上,main 函数注册一个 defer ,且稍后主动触发 panic,main 函数退出之际就会调用 defer 注册的匿名函数。再提一点,这里其实有两个要点: 1. defer 在 panic 异常场景也能确保调用; 2. recover 必须和 defer 结合才有意义; ## 3. 总结 1. defer 其实并不是 golang 独创,是多种高级语言的共同选择; 2. defer 最重要的一个特点就是无视异常可执行,这个是 golang 在提供了 panic-recover 机制之后必须做的补偿机制; 3. defer 的作用域存在于函数,defer 也只有和函数结合才有意义; 4. defer 允许你把配套的两个行为代码放在最近相邻的两行,比如创建&释放资源、加锁&释放锁等,使得代码更易读,编程体验优秀。 #### 参考资料 1. [go语言编程](http://www.codebaoku.com/golang/golang-index.html) 2. [编程宝库](http://www.codebaoku.com/)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1552 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传