《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的更多相关文章

  1. C程序设计语言学习笔记

    在Windows下运行C语言程序 Windows下的编程工具使用 VC 6.0,下面讲解如何在VC 6.0下运行上节的"Hello, world"程序. 1) 新建Win32 Co ...

  2. 2017-04-21周C语言学习笔记

    C语言学习笔记:... --------------------------------- C语言学习笔记:学习程度的高低取决于.自学能力的高低.有的时候生活就是这样的.聪明的人有时候需要.用笨的方法 ...

  3. 2017-05-4-C语言学习笔记

    C语言学习笔记... ------------------------------------ Hello C语言:什么是程序:程序是指:完成某件事的既定方式和过程.计算机中的程序是指:为了让计算机执 ...

  4. GO语言学习笔记(一)

    GO语言学习笔记 1.数组切片slice:可动态增长的数组 2.错误处理流程关键字:defer panic recover 3.变量的初始化:以下效果一样 `var a int = 10` `var ...

  5. Go语言学习笔记(1)——顺序编程

    Go语言学习笔记这一堆主要是<Go语言编程>(人民邮电出版社)的读书笔记.中间会穿插一些零碎的点,比如源码学习之类的.大概就是这样吧. 1. 顺序编程 1.1 变量 变量的声明: var ...

  6. GO语言学习笔记-并发篇 Study for Go ! Chapter seven - Concurrency

    持续更新 Go 语言学习进度中 ...... GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs ...

  7. HTML语言学习笔记(会更新)

    # HTML语言学习笔记(会更新) 一个html文件是由一系列的元素和标签组成的. 标签: 1.<html></html> 表示该文件为超文本标记语言(HTML)编写的.成对出 ...

  8. Haskell语言学习笔记(88)语言扩展(1)

    ExistentialQuantification {-# LANGUAGE ExistentialQuantification #-} 存在类型专用的语言扩展 Haskell语言学习笔记(73)Ex ...

  9. Go语言学习笔记十三: Map集合

    Go语言学习笔记十三: Map集合 Map在每种语言中基本都有,Java中是属于集合类Map,其包括HashMap, TreeMap等.而Python语言直接就属于一种类型,写法上比Java还简单. ...

  10. Go语言学习笔记十二: 范围(Range)

    Go语言学习笔记十二: 范围(Range) rang这个关键字主要用来遍历数组,切片,通道或Map.在数组和切片中返回索引值,在Map中返回key. 这个特别像python的方式.不过写法上比较怪异使 ...

随机推荐

  1. MQTT-基础理念

    MQTT与HTTP的区别 HTTP协议是客户端与服务端直连请求与响应 MQTT是基于发布订阅模型的轻量级的消息传输协议 MQTT能力 发布:Publish 订阅:Subscribe 代理:Broker ...

  2. Ubuntu20.04/22.04 ESP32 命令行开发环境配置

    ESP32 芯片系列 ESP32分三个系列 ESP32-S ESP32-S3: Xtensa 32位 LX7 双核 240 MHz, 384KB ROM, 512KB SRAM, QFN7x7, 56 ...

  3. Git Conventional Commits (Git代码提交说明规范)

    Conventional Commits (代码提交说明规范) Conventional Commits 是关于Git Commit 提交代码时, 填写的说明文字的一个规范. 这个规范提供了一套易于理 ...

  4. 【Unity3D】Bloom特效

    1 Bloom 特效原理 ​ Bloom 特效是指:将画面中较亮的区域向外扩散,造成一种朦脓的效果.实现 Bloom 特效,一般要经过 3 个阶段处理:亮区域检测.高斯模糊.Bloom 合成. ​ 本 ...

  5. Mysql表读写、索引等操作的sql语句效率优化问题

    上次我们说到mysql的一些sql查询方面的优化,包括查看explain执行计划,分析索引等等.今天我们分享一些 分析mysql表读写.索引等等操作的sql语句. 闲话不多说,直接上代码: 反映表的读 ...

  6. 摸鱼快报:golang net/http中的雕虫小技

    以后会开一个板块,摸鱼快报,快速记录这几周开发中雕虫小技. 1. 向开发环境localhost:3000种植cookie 前端使用Create React App脚手架,默认以localhost:30 ...

  7. 原来你是这样的JAVA--[07]聊聊Integer和BigDecimal

    今天来聊聊Java中跟数值处理相关的两个类型Integer和BigDecimal. 说起这两个类型,我们肯定都不陌生,但是其中有些容易踩到的坑需要注意避让. Integer 整型我们应该每天都会用到, ...

  8. 2024-02-24:用go语言,给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1, 同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti

    2024-02-24:用go语言,给你一个 n 个点的带权无向连通图,节点编号为 0 到 n-1, 同时还有一个数组 edges ,其中 edges[i] = [fromi, toi, weighti ...

  9. matplotlib画图中x轴过于密集的解决办法

    import matplotlib.ticker as ticker ax.xaxis.set_major_locator(ticker.MultipleLocator(base=10)) # tic ...

  10. Jina AI x 矩池云Matpool |神经搜索引擎,一键构建

    图片.视频.语音等非结构化数据在快速增长,随着深度学习技术的不断升级,非结构化数据的搜索也逐渐形成可能.在这样的背景下,专注于神经搜索技术的商业开源软件公司--Jina AI,提出了神经搜索 (Neu ...