《Go程序设计语言》学习笔记之defer
2021/12/27 1:07:48
本文主要是介绍《Go程序设计语言》学习笔记之defer,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
《Go程序设计语言》学习笔记之defer
一. 环境
Centos8.5, go1.17.5 linux/amd64
二. 概念
语法上,一个 defer 语句就是一个普通的函数或方法调用,在调用之前加上关键字 defer 。
执行时机
无论是正常情况下,如执行 return 或函数执行完毕,还是不正常的情况下,比如发生宕机,实际的调用推迟到包含 defer 语句的函数结束后才执行。
defer 语句没有限制使用次数
执行的时候以调用 defer 语句顺序的倒序进行
23 func main() { 24 defer fmt.Println("a") 25 defer fmt.Println("b") 26 defer fmt.Println("c") 27 fmt.Println("----------") 28 }
运行结果如下
三. 使用场景
1. 常用于成对的操作,比如打开和关闭,连接和断开,加锁和解锁。 使用方式,在成功获得资源之后,使用 defer 语句。
27 resp, err := http.Get(url) 28 if err != nil { 29 return err 30 } 31 defer resp.Body.Close() 32----------------------------- 33 f, err := os.Open(filename) 34 if err != nil { 35 return nil, err 36 } 37 defer f.Close() 38 ---------------------------- 39 var mu sync.Mutex 40 var m = make(map[string]int) 41 func lookup(key string) int { 42 mu.Lock() 43 defer mu.Unlock() 44 return m[key] 45 }
2. 调试一个复杂的函数
1) 书中示例代码如下,开始我没太理解,看了几遍,再自己敲敲代码,再看看书,然后一下子反应过来了。bigSlowOperation函数有一个复杂中的操作,代码中以 第13行代码模拟了费时的操作。在bigSlowOperation函数的“入口”和“出口”处设置调试行为。下面是通过延迟调用 trace 函数来实现的,哦,不,这个说法不对,是延迟调用 trace 函数返回的匿名函数来实现的。这里需要注意一下第13行代码中最后面有一个小括号,这里表示是对trace函数返回的匿名函数的调用,而 defer 关键字则来修饰它的。
bigSlowOperation 函数执行时,trace 函数中的代码 第17行、18行正常执行,通过结果可以看到打印了进入 bigSlowOperation 函数时的时间。而匿名函数的调用则延迟到了 bigSlowOperation 函数结束。通过结果,可以看到,停顿了3秒后,打印了结束的时间及 exit 字样。
10 func bigSlowOperation() { 11 defer trace("bigSlowOperation")() 12 fmt.Println("----------") 13 time.Sleep(3 * time.Second) 14 } 15 16 func trace(msg string) func() { 17 start := time.Now() 18 log.Printf("enter %s\n", msg) 19 return func() { log.Printf("exit %s, (%s)\n", msg, time.Since(start)) } 20 21 } 22 23 func main() { 24 bigSlowOperation() 25 }
运行结果如下
2) 再次验证一下。去掉上面示例代码中,第11行中的 defer 关键字。可以先自行想下结果
bigSlowOperation 函数执行时,调用了 trace 函数返回的匿名函数,这个过程中呢,先执行了 trace 函数中的内容(打印 enter 字样),然后执行其返回的匿名函数(打印了 exit 字样)。
运行结果如下
3) 然后,再次验证一下。在上面的示例代码基础上,在第11行加上 defer 关键字,去掉最后面的 (),可自行想下结果。
这时是延迟调用了 trace 函数了,就不是延迟调用其返回的匿名函数了。trace 函数在 bigSlowOperation 函数结束后才执行,仅仅打印了 enter 字样。trace 函数返回的匿名函数因为没有调用,所以永远不会执行。
运行结果如下,符合预期。
四. 其它
示例
1) 初始情况
28 func double(x int) int { 29 return x + x 30 }
2) 调整
通过命名结果变量和增加 defer 语句,在每次调用函数的时候输出它的参数和结果。也就是说,在double 函数结束时,执行了匿名函数的调用,打印了参数和结果。
32 func double(x int) (result int) { 33 defer func() { fmt.Printf("double(%d) = %d\n", x, result) }() 34 return x + x 35 }
调用上面的函数,打印结果如下
3) 再调整,增加一个函数 triple,并打印其返回结果。
延迟执行的匿名函数可以改变外层函数返回给调用者的结果。在函数 triple 的 result 返回前,延迟调用的匿名函数 func() 修改了 result 的值,于是函数 triple 返回值为12(8 + 4)。
23 func main() { 24 //double(4) 25 fmt.Println(triple(4)) 26 } 27 28 func double2(x int) int { 29 return x + x 30 } 31 32 func double(x int) (result int) { 33 defer func() { fmt.Printf("double(%d) = %d\n", x, result) }() 34 return x + x 35 } 36 37 func triple(x int) (result int) { 38 defer func() { result += x }() 39 return double(x) 40 }
运行结果如下
这篇关于《Go程序设计语言》学习笔记之defer的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15SendGrid 的 Go 客户端库怎么实现同时向多个邮箱发送邮件?-icode9专业技术文章分享
- 2024-11-15SendGrid 的 Go 客户端库怎么设置header 和 标签tag 呢?-icode9专业技术文章分享
- 2024-11-12Cargo deny安装指路
- 2024-11-02MongoDB项目实战:从入门到初级应用
- 2024-11-01随时随地一键转录,Google Cloud 新模型 Chirp 2 让语音识别更上一层楼
- 2024-10-25Google Cloud动手实验详解:如何在Cloud Run上开发无服务器应用
- 2024-10-24AI ?先驱齐聚 BAAI 2024,发布大规模语言、多模态、具身、生物计算以及 FlagOpen 2.0 等 AI 模型创新成果。
- 2024-10-20goland工具下,如修改一个项目的标准库SDK的版本-icode9专业技术文章分享
- 2024-10-17Go学习:初学者的简单教程
- 2024-10-17Go学习:新手入门完全指南