Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >28.Go异常处理-延迟调用defer

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

作者头像
Devops海洋的渔夫
发布于 2021-09-17 02:23:54
发布于 2021-09-17 02:23:54
60100
代码可运行
举报
文章被收录于专栏:Devops专栏Devops专栏
运行总次数:0
代码可运行

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

3 延迟调用defer

3.1 defer基本使用

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fmt.Println("hello world")
fmt.Println("I am regal") 

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

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

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

执行如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
I am regal
print 3.....
hello world # 最后延迟调用
defer的应用场景:

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
if err != nil {
   return
}

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
defer fmt.Println("hello world") // 延迟调用
defer fmt.Println("I am regal")
defer fmt.Println("print 3.....") 

执行的结果是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
print 3.....
I am regal
hello world

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

如下程序执行的结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
print 3.....
I am regal
hello world
panic: runtime error: integer divide by zero # Test导致的panic错误

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

3.3 defer与匿名函数结合使用

基本用法如下:

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
main函数中a 100
main函数中b 200
匿名函数中a 100
匿名函数中b 200 

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

现在将程序做如下修改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
代码运行次数:0
运行
AI代码解释
复制
main函数中a 100
main函数中b 200
匿名函数中a 10
匿名函数中b 20 

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

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func f1() (r int) {
   defer func() {
      r++
   }()
   r = 0
   return
}

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

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
使用defer释放资源
首先,我们看一个拷贝文件函数的示例。我们还将管理该文件描述符的关闭,因为一个 *os.File一旦被打开准备读写时,它就必须要使用Close函数进行关闭。最后,在函数的最后,我们将使用Sync方法来刷新文件系统的缓冲区以便将内容强制写到磁盘上,使副本持久化。下面是该示例的第一版实现:
Go学堂
2023/01/31
6680
Golang 高效实践之defer、panic、recover实践
我们知道Golang处理异常是用error返回的方式,然后调用方根据error的值走不同的处理逻辑。但是,如果程序触发其他的严重异常,比如说数组越界,程序就要直接崩溃。Golang有没有一种异常捕获和恢复机制呢?这个就是本文要讲的panic和recover。其中recover要配合defer使用才能发挥出效果。
用户2937493
2019/08/29
9760
Golang 高效实践之defer、panic、recover实践
Defer,Panic,and Recover
Go拥有一般的控制流程机制,像if、for、switch、goto。除此之外go也拥有一个单独的goroutine机制运行go语句。这里我想讨论一些不太常见的语法:defer,panic,and recover
陌无崖
2020/07/27
4970
Go语言错误与异常处理机制
1 Error接口 Go语言中的error类型实际上是抽象了Error()方法的error接口
李海彬
2018/07/26
3820
Go语言错误与异常处理机制
Go语言defer分析
defer语句是专门在函数结束以后做一些清理工作的。我们先举一个例子来更好的理解,现在有一个函数,它的作用是把一个文件内容拷贝到另一个文件。
平也
2020/04/03
4200
Go语言defer分析
Go 语言简介(上)— 语法
Hello World package main //声明本文件的package名 import "fmt" //import语言的fmt库——用于输出 func main() { fmt.Println("hello world") } 运行 你可以有两种运行方式, $go run hello.go hello world $go build hello.go $ls hello hello.go $./hello hello world 自己的package 你可以使用GOPATH环境变
李海彬
2018/03/22
1.2K0
GO 语言简介 — 语法
下面的文章主要是以代码和注释为主。只需要你对C语言,Unix,Python有一点基础,我相信你会在30分钟左右读完并对Go语言有一些初步了解的。
禹都一只猫olei
2018/08/02
1.4K0
golang操作文件
用习惯了python, 用go的文件读写,觉得还是有点别扭。 先总结下来,后面用的时候,可以查看。
赵云龙龙
2022/03/29
5130
golang操作文件
2010年08月04日 Go生态洞察:Defer, Panic, Recover 深度解析
嘿,Go语言爱好者们,猫头虎博主今天来带大家深入探讨Go的三个控制流机制:defer, panic和recover。这些机制不像if和for那样常见,但它们提供了Go语言特有的强大功能,特别是在错误处理和资源管理方面。这篇文章将详细介绍这三个关键字的用法和它们的内部工作原理。 Go错误处理、Go资源管理、Go控制流。
猫头虎
2024/04/09
800
2010年08月04日 Go生态洞察:Defer, Panic, Recover 深度解析
Golang之文件读写
读写文件,不添加文件路径,默认写入到GOPATH路径下 终端读写: 源码 func Sscanf func Sscanf(str string, format string, a ...interface{}) (n int, err error) 解释:Sscanf scans the argument string, storing successive space-separated values into successive arguments as determined by the form
超蛋lhy
2018/08/31
1.2K0
Golang之文件读写
go语言文件操作汇总
bufio.Writer.WriteString 带缓冲的写,最后要将缓冲中的数据写入下层的io.Writer接口(Flush方法)
程序员小饭
2020/09/07
4130
Golang之轻松化解defer的温柔陷阱
defer是Go语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行。
李海彬
2019/05/14
8240
2.Go语言之文件操作学习记录.md
描述: 我们可以采用os包中的Open()函数打开一个文件,返回一个*File和一个err。然后对得到的文件实例调用Close()函数就能够关闭文件。
全栈工程师修炼指南
2022/09/29
4760
golang 函数定义及其接口实例
/有返回值 且返回一个 func max(a int, b int) int { if a > b { return a } return b } //有返回值 且返回二个 func multi_ret(key string) (int, bool) { m := map[string]int{"ont": 1, "two": 2, "three": 3} var err bool var val int val, err = m[k
李海彬
2018/03/23
6670
Go语言入门——进阶语法篇(四)
Go语言没有类似Java或Python那种try...catch...机制处理异常,Go的哲学是与众不同的,Go的设计者认为主流的异常处理机制是一种被过度滥用的技巧,而且存在很大的潜在危害,Go的异常处理(或者说是错误处理)是一种非常简单直观的方式。通常的,我们在写Java、Python之类的代码时,遇到可能存在的异常,直接用try括起来,使用catch捕获,然后就万事大吉了,当系统长时间的运行时,大大增加了不稳定性,所积累的问题可能在某一刻爆发。而Go者使用一种称为"恐慌的"机制,在有必要时,直接让系统宕机,让问题发生时立刻暴露出来,不必累积。很难说哪种设计更好,但Go语言确实简化了代码。
arcticfox
2019/09/03
5260
Golang之轻松化解defer的温柔陷阱
defer是Go语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后(包括通过return正常结束或者panic导致的异常结束)执行。深受Go开发者的欢迎,但一不小心就会掉进它的温柔陷阱,只有深入理解它的原理,我们才能轻松避开,写出漂亮稳健的代码。
梦醒人间
2019/05/21
4020
GoLang读写数据---中
注意 defer 的使用:当打开dst文件时发生了错误,那么 defer 仍然能够确保 src.Close() 执行。如果不这么做,src文件会一直保持打开状态并占用资源。
大忽悠爱学习
2022/08/23
3810
GoLang读写数据---中
浅析golang中的defer
延迟执行可以用在很多的场景,比如连接数据库、打开文件、获取http连接等资源后,都需要释放资源,但是写代码的人容易忘记关闭资源的连接,且容易造成代码冗余。所以可以用defer语句在资源打开后马上调用defer去释放资源,可以避免忘记释放资源。因此,在诸如打开连接/关闭连接;申请/释放锁;打开文件/关闭文件等成对出现的操作场景里,defer会显得格外方便,如下:
素履coder
2022/02/17
5020
Golang defer 快速上手
defer 用于预设一个函数调用,推迟函数的执行。被推迟的函数会在执行 defer 的函数返回之前执行。
恋喵大鲤鱼
2022/01/12
7730
Go 函数的健壮性、panic异常处理、defer 机制
函数的使用者可能是任何人,这些人在使用函数之前可能都没有阅读过任何手册或文档,他们会向函数传入你意想不到的参数。因此,为了保证函数的健壮性,函数需要对所有输入的参数进行合法性的检查。一旦发现问题,立即终止函数的执行,返回预设的错误值。
贾维斯Echo
2023/10/23
5090
Go 函数的健壮性、panic异常处理、defer 机制
相关推荐
使用defer释放资源
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档