《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的更多相关文章
- C程序设计语言学习笔记
在Windows下运行C语言程序 Windows下的编程工具使用 VC 6.0,下面讲解如何在VC 6.0下运行上节的"Hello, world"程序. 1) 新建Win32 Co ...
- 2017-04-21周C语言学习笔记
C语言学习笔记:... --------------------------------- C语言学习笔记:学习程度的高低取决于.自学能力的高低.有的时候生活就是这样的.聪明的人有时候需要.用笨的方法 ...
- 2017-05-4-C语言学习笔记
C语言学习笔记... ------------------------------------ Hello C语言:什么是程序:程序是指:完成某件事的既定方式和过程.计算机中的程序是指:为了让计算机执 ...
- GO语言学习笔记(一)
GO语言学习笔记 1.数组切片slice:可动态增长的数组 2.错误处理流程关键字:defer panic recover 3.变量的初始化:以下效果一样 `var a int = 10` `var ...
- Go语言学习笔记(1)——顺序编程
Go语言学习笔记这一堆主要是<Go语言编程>(人民邮电出版社)的读书笔记.中间会穿插一些零碎的点,比如源码学习之类的.大概就是这样吧. 1. 顺序编程 1.1 变量 变量的声明: var ...
- GO语言学习笔记-并发篇 Study for Go ! Chapter seven - Concurrency
持续更新 Go 语言学习进度中 ...... GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs ...
- HTML语言学习笔记(会更新)
# HTML语言学习笔记(会更新) 一个html文件是由一系列的元素和标签组成的. 标签: 1.<html></html> 表示该文件为超文本标记语言(HTML)编写的.成对出 ...
- Haskell语言学习笔记(88)语言扩展(1)
ExistentialQuantification {-# LANGUAGE ExistentialQuantification #-} 存在类型专用的语言扩展 Haskell语言学习笔记(73)Ex ...
- Go语言学习笔记十三: Map集合
Go语言学习笔记十三: Map集合 Map在每种语言中基本都有,Java中是属于集合类Map,其包括HashMap, TreeMap等.而Python语言直接就属于一种类型,写法上比Java还简单. ...
- Go语言学习笔记十二: 范围(Range)
Go语言学习笔记十二: 范围(Range) rang这个关键字主要用来遍历数组,切片,通道或Map.在数组和切片中返回索引值,在Map中返回key. 这个特别像python的方式.不过写法上比较怪异使 ...
随机推荐
- JS leetcode 实现strStr()函数 题解分析
壹 ❀ 引 前几天心情比较浮躁,烦心事太多,偷懒了3天,还是继续刷leetcode.那么今天做的题目为实现 strStr() 函数.,原题如下: 给定一个 haystack 字符串和一个 needle ...
- Java核心技术卷1:基础知识(原书第10版)
本书为专业程序员解决实际问题而写,Java基础知识面覆盖很完整,可以帮助你深入了解Java语言和库.在卷I中,Horstmann主要强调基本语言概念和现代用户界面编程基础,深入介绍了从Java面向对象 ...
- 利用LiveReload插件实现vscode和谷歌浏览器实时刷新
说明 最近在研究CSS希望可以提升一个层次.在写DEMO练习的时候老是需要去谷歌浏览器手动刷新页面才能看到更改后的效果次数多了 我也受不了,这不我又请来了个帮手: LiveReload,名如其人,这家 ...
- "explicit" 的使用
今天在编译项目时,代码审查提示 "Single-parameter constructors should be marked explicit" 于是就在构造函数前加上 expl ...
- win32-GetActiveWindow和GetForegroundWindow
最近被这两个api搞得有点晕,故查阅了相关的资料. 这篇文章解释的很好:https://devblogs.microsoft.com/oldnewthing/20081006-00/?p=20643 ...
- PostgreSQL中查看版本的几种方式
PostgreSQL中查看版本的几种方式 1.SQL方式 1 2 3 4 5 6 7 8 9 10 postgres=# show server_version; server_version -- ...
- mysql日期范围查找(两个日期之间的记录)
转自:https://blog.csdn.net/lzxlfly/article/details/97577575?utm_medium=distribute.pc_relevant_t0.none- ...
- 【LeetCode二叉树#06】获取二叉树的所有路径(分析递归中的回溯机制)
二叉树所有路径 力扣题目链接(opens new window) 给定一个二叉树,返回所有从根节点到叶子节点的路径. 说明: 叶子节点是指没有子节点的节点. 示例: 思路 根据题意,每次遍历至子节点, ...
- 老生常谈的iOS- weak原理,你真的懂得还是为了应付面试
前言 weak对于iOS开发来说只要解决一些对象相互引用的时候,避免出现强强引用,对象不能被释放,出现内存泄露的问题. weak 关键字的作用域弱引用,所引用对象的计数器不会加一,并在引用对象被释放的 ...
- elasticsearch 查询索引和清理索引命令
查询 curl --silent 'http://127.0.0.1:9200/_cat/indices' 删除 curl -X DELETE "localhost:9200/wifiloc ...