前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >28.Go异常处理-延迟调用defer

28.Go异常处理-延迟调用defer

作者头像
Devops海洋的渔夫
发布2021-09-17 10:23:54
5550
发布2021-09-17 10:23:54
举报
文章被收录于专栏:Devops专栏Devops专栏

28.Go异常处理-延迟调用defer

3 延迟调用defer

3.1 defer基本使用

函数定义完成后,只有调用函数才能够执行,并且一经调用立即执行。例如:

代码语言:javascript
复制
fmt.Println("hello world")
fmt.Println("I am regal") 

先输出“hello world”,然后再输出“I am regal”

但是关键字 defer ⽤于延迟一个函数(或者当前所创建的匿名函数)的执行。注意,defer语句只能出现在函数的内部。

基本用法如下:
代码语言:javascript
复制
defer fmt.Println("hello world") // 延迟调用
fmt.Println("I am regal")
fmt.Println("print 3.....")

执行如下:

代码语言:javascript
复制
I am regal
print 3.....
hello world # 最后延迟调用
defer的应用场景:

defer的应用场景:文件操作,先打开文件,执行读写操作,最后关闭文件。为了保证文件的关闭能够正确执行,可以使用defer.

大家可以先看一下文件操作的伪代码,来体会一下关于defer的 场景,关于文件操作,我们后面会详细的讲解。

代码语言:javascript
复制
import (
   "fmt"
   "io"
   "os"
)

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 
} 

以上代码就是,打开文件,创建文件,执行文件拷贝的操作,最后将文件进行关闭。

但是问题时,如果假设在执行文件打开时,出现了问题,那么就会执行如下代码:

代码语言:javascript
复制
if err != nil {
   return
}

退出整个函数,那么就不会执行文件的关闭操作。

所以为了解决这个问题,现在将程序进行修改,如下所示:

代码语言:javascript
复制
func CopyFile(dstName, srcName string) (written int64, err error) {
   //根据传递过来的参数(文件名)打开文件
   src, err := os.Open(srcName)

   // 如果打开文件时出现错误,退出整个函数
   if err != nil {
      return
   }
   
   // 修改:使用defer保存CopyFile函数退出时,执行文件关闭
   defer src.Close()
   
   // 创建文件
   dst, err := os.Create(dstName)
   if err != nil {
      return
   }

   // 修改:使用defer保存CopyFile函数退出时,执行文件关闭
   defer dst.Close()

   return io.Copy(dst, src)
} 

通过在文件关闭函数之前加上defer,保证了不管什么情况下都会执行文件关闭的操作。

代码逻辑越复杂,defer使用越重要。

同理,进行网络编程时,最后也要关闭整个网络的链接,也会用到defer。

3.2 defer执行顺序

先看如下程序执行结果是:

代码语言:javascript
复制
defer fmt.Println("hello world") // 延迟调用
defer fmt.Println("I am regal")
defer fmt.Println("print 3.....") 

执行的结果是:

代码语言:javascript
复制
print 3.....
I am regal
hello world

总结:如果一个函数中有多个defer语句,它们会以LIFO(后进先出)的顺序执行。

如下程序执行的结果:

代码语言:javascript
复制
func Test(x int)  {
   result := 100 / x
   fmt.Println("Test...result = ", result)
}

func main() {
   defer fmt.Println("hello world") // 延迟调用
   defer fmt.Println("I am regal")
   defer Test(0) // 传递0将会导致 panic 报错
   defer fmt.Println("print 3.....")
} 

执行结果:

代码语言:javascript
复制
print 3.....
I am regal
hello world
panic: runtime error: integer divide by zero # Test导致的panic错误

即使函数或某个延迟调用发生错误,这些调用依旧会被执⾏。

3.3 defer与匿名函数结合使用

基本用法如下:

我们先看以下程序的执行结果:

代码语言:javascript
复制
func main() {
   a := 10
   b := 20

   defer func() {
      fmt.Println("匿名函数中a", a)
      fmt.Println("匿名函数中b", b)
   }()

   a = 100
   b = 200
   fmt.Println("main函数中a", a)
   fmt.Println("main函数中b", b)
} 

执行的结果如下:

代码语言:javascript
复制
main函数中a 100
main函数中b 200
匿名函数中a 100
匿名函数中b 200 

前面讲解过,defer会延迟函数的执行,虽然立即调用了匿名函数,但是该匿名函数不会执行,等整个main( )函数结束之前在去调用执行匿名函数,所以输出结果如上所示。

现在将程序做如下修改:

代码语言:javascript
复制
func main() {
   a := 10
   b := 20

   defer func(a, b int) {
      fmt.Println("匿名函数中a", a)
      fmt.Println("匿名函数中b", b)
   }(a, b) // 修改传递参数 a b

   a = 100
   b = 200
   fmt.Println("main函数中a", a)
   fmt.Println("main函数中b", b)
} 

该程序的执行结果如下:

代码语言:javascript
复制
main函数中a 100
main函数中b 200
匿名函数中a 10
匿名函数中b 20 

从执行结果上分析,由于匿名函数前面加上了defer所以,匿名函数没有立即执行。但是问题是,程序从上开始执行当执行到匿名函数时,虽然没有立即调用执行匿名函数,但是已经完成了参数的传递。

思考:以下程序的输出结果是:

(1)阅读程序,分析结果

代码语言:javascript
复制
func f1() (r int) {
   defer func() {
      r++
   }()
   r = 0
   return
}

func main() {
   i := f1()
   fmt.Println(i)
}

(2)阅读程序,分析结果

代码语言:javascript
复制
func double(x int) int  {
   return x + x
}

func triple(x int) (r int) {
   defer func() {
      r += x
   }()
   return double(x)
}

func main() {
   fmt.Println(triple(3))
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 海洋的渔夫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 28.Go异常处理-延迟调用defer
    • 3 延迟调用defer
      • 3.1 defer基本使用
      • 3.2 defer执行顺序
      • 3.3 defer与匿名函数结合使用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档