《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的方式.不过写法上比较怪异使 ...
随机推荐
- NC216012 Let'sPlayCurling
题目链接 题目 题目描述 Curling is a sport in which players slide stones on a sheet of ice toward a target area ...
- 从零开始手写缓存框架(二)redis expire 过期原理及实现
前言 我们在 从零手写 cache 框架(一)实现固定大小的缓存 中已经初步实现了我们的 cache. 本节,让我们来一起学习一下如何实现类似 redis 中的 expire 过期功能. 过期是一个非 ...
- Oracle高级队列介绍
原始链接:http://www.oracle-developer.net/display.php?id=411 oracle高级队列介绍 高级队列Advanced Queuing(AQ)在oracle ...
- oracle exp/imp命令使用parfile实现参数文件调用
优先使用数据泵(expdp/impdp)方式,更高效,问题少. 关于exp/imp工具的使用请参考我的另一篇文章: https://blog.csdn.net/IndexMan/article/det ...
- 以二进制文件安装K8S之环境准备
为了k8s集群能正常运行,需要先完成4项准备工作: 1.关闭防火墙 2.禁用SeLinux 3.关闭Swap 4.安装Docker 关闭防火墙 # 查看防火墙状态 getenforce #关闭防火墙, ...
- 看看 ChatGPT 给的前端面试题
以下是一些可能出现在中国互联网公司前端开发工程师面试中的题目: 解释一下 CSS 盒模型,并说明其中的各个部分. 请解释一下响应式设计是什么,以及你是如何实现响应式设计的. 什么是跨域资源共享(COR ...
- 类型注解Callable
from collections.abc import Callable """ Callable[[ParamType1, ParamType2], ReturnTyp ...
- 用容器部署Nexus 3作为Nuget和Docker的仓库
1.准备docker-compose的配置文件 version: '3' services: nexus: image: 'sonatype/nexus3:3.42.0' container_name ...
- 文心一言 VS 讯飞星火 VS chatgpt (206)-- 算法导论15.4 3题
三.设计 LCS-LENGTH的带备忘的版本,运行时间为O(mn).需要写代码的时候,请用go语言. 文心一言,代码正常运行: LCS-LENGTH(Longest Common Subsequenc ...
- Tomcat 系列
Tomcat 系列 目录 Tomcat 系列 一. 基本概念 1.1 java jdk 概念 1.2 jsp.servlet是什么 1.3 web服务器.web容器.应用程序服务器 1.4 tomca ...