Go基础编程 - 15 - 延迟调用(defer)

延迟调用 defer

    • 1. 特性
    • 2. 常用用途
    • 3. defer 执行顺序:`同函数内`先进后出
    • 4. defer 闭包
    • 5. defer 陷阱


上一篇:泛型


1. 特性

1. 关键字 defer 用于注册延迟调用。
2. defer 调用直到 return 前才被执行。
3. 同函数内多个 defer 语句,按先进后出的方式执行。
4. defer 语句中的变量,在 defer 声明时就决定了。

2. 常用用途

滥用 defer 可能会造成性能问题,尤其是在一个“大循环”里。

1. 关闭文件句柄。
2. 锁资源释放。
3. 数据库连接释放。

3. defer 执行顺序:同函数内先进后出

package mainimport "fmt"func defer1(i int) {defer fmt.Printf("defer1 %d - 1\n", i)defer fmt.Printf("defer1 %d - 2\n", i)
}func main() {// 函数内 defer,在函数执行结束前执行。// 因此嵌套函数 defer1() 内的 defer 在自身函数内按先进后出的顺序执行。// 且 defer1() 内的 defer 在 main 函数内的执行顺序仅取决于 defer1() 函数的执行顺序。defer defer1(100)defer1(200)		 defer fmt.Println("main 1")defer1(300)			defer defer1(400)	
}

以上代码执行顺序及输出结果如下图:

  1. 函数内 defer,在函数执行结束前执行。
  2. main 函数内,关键字 defer 在 main 函数内按先进后出顺序在 main 函数执行结束前执行,其它代码按顺序执行。
  3. defer1() 函数内关键字 defer 在 defer1 函数内按先进后出顺序执行。
  4. defer1() 函数内的输出在自身函数执行结束前全部输出。
    在这里插入图片描述

多个 defer 注册,按 FILO 次序执行 ( 先进后出 )。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。

func main() {defer println(1)defer println(2)i := 0defer func() {println(100 / i)	// 此处抛出 panic,但下面 defer 依然执行}()defer println(3)
}

输出结果

3
2
1
panic: runtime error: integer divide by zero

4. defer 闭包

defer 语句适用于单个函数或语句的延迟执行。当需要执行更复杂的逻辑或者多个操作时,可以使用 defer func () 闭包。

  • 每次执行 defer 语句时,函数值调用的参数都会进行求值并保存,但不执行实际的函数。 也就是复制了一份。

    func main() {i := 1defer fmt.Println("one - i =", i) // i 被复制保存// defer 使用了闭包, 此时仅定义了闭包函数,并未执行。defer func() {fmt.Println("two - i =", i)  // 闭包引用 i}()// 变量 i 作为函数参数传入时,会进行求值并保存。defer func(n int) {fmt.Println("three - i =", n)}(i) // i 被复制保存i = 100defer func(i int) {fmt.Println("four - i =", i)}(i) // i 被复制保存fmt.Println("i =", i)
    }
    

    输出结果:

    i = 100
    four - i = 100
    three - i = 1
    two - i = 100
    one - i = 1
    
  • defer 中可以使用指针或闭包“延迟”读取。

    func main() {x, y, z := 10, 10, 10// 延迟读取 y,z 的值defer func(i int, j *int) {fmt.Printf("defer: x = %d, y = %d, z = %d \n", i, *j, z) // y 指针传递,z 闭包引用}(x, &y) // x 被复制x += 100y += 100z += 100fmt.Printf("main: x = %d, y = %d, z = %d\n", x, y, z)
    }
    

    输出结果:

    main: x = 110, y = 110, z = 110
    defer: x = 10, y = 110, z = 110
    

5. defer 陷阱

  • defer 与闭包、return

    package mainimport ("errors""fmt""os"
    )// 如果 defer 后不是一个闭包,最后执行的时候我们得到的并不是最新的值,而是声明 defer 时保存的值。
    // 有具名返回值函数中,实际值为return前最终计算结果。
    func closure(i, j int) (n int, err error) {defer fmt.Printf("1 n = %d, defer: %v\n", err) // n, err 复制保存当前值defer func(err error) {fmt.Printf("2 n = %d, defer: %v\n", err)}(err) // err 保存当前值传参;n 为全局变量defer func() {fmt.Printf("3 n = %d, defer: %v\n", err)}() // 闭包引用if j == 0 {err = errors.New("divided by zero")return}return i / j, nil
    }// 使用相同的变量释放不同的资源,那么这个操作可能无法正常执行。
    // 1. 示例中打开test.txt、test2.txt都赋值给变量 f。
    // 2. defer 声明使用闭包函数;在执行defer声明的代码时,f 已经被重新赋值为 test2.txt,导致 test.txt 关闭失败。
    //    报错:close failed:test.txt close test2.txt: file already closed
    // 3. 优化建议:避免使用相同变量;或 defer 声明使用值参传参
    func closureCover() {f, err := os.Open("test.txt")if err != nil {fmt.Println("open failed: test.txt")}if f != nil {defer func() {if err := f.Close(); err != nil {fmt.Println("close failed:test.txt", err)}}()// 优化为值传递/*defer func(f *os.File) {...}(f)*/}f, err = os.Open("test2.txt")if err != nil {fmt.Println("open failed: test2.txt")}if f != nil {defer func() {if err := f.Close(); err != nil {fmt.Println("close failed:test2.txt", err)}}() // 优化同上}
    }func main() {closure(2, 0)// 输出:// 3 n = 0, defer: divided by zero// 2 n = 0, defer: <nil>// 1 n = 0, defer: <nil>// 有具名返回值函数中,实际值为return前最终计算结果。输出:// 3 n = 5, defer: <nil>// 2 n = 5, defer: <nil>// 1 n = 0, defer: <nil>
    }
    
  • defer nil 函数

    func main() {defer func() {if err := recover(); err != nil {fmt.Println("recover:", err)}}()var run func() = nildefer run()fmt.Println("running")// 输出:// running// recover: runtime error: invalid memory address or nil pointer dereference
    }
    
  • 在错误位置使用 defer

    package mainimport ("fmt""net/http"
    )func main() {res, err := http.Get("http://www.xxxxxxxxxxx")defer res.Body.Close()if err == nil {return}fmt.Println("running")// 输出// panic: runtime error: invalid memory address or nil pointer dereference
    }
    

    当 http.Get 失败时 res 为 nil,我们在 defer 调用 res.Body 时并未判断是否执行成功,会抛出异常。优化如下:

    func main() {res, err := http.Get("http://www.xxxxxxxxxxx")if err == nil {	 // 判断 http.Get 执行成功时,才需要关闭响应连接defer res.Body.Close()}fmt.Println("running")// 输出// running
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1548721.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

怎么绕开华为纯净模式安装软件

我是标题 众所周不知&#xff0c;华为鸿蒙系统自带纯净模式&#xff0c;而且 没法关闭 : ) 我反正没找到关闭键 以前或许会有提示&#xff0c;无视风险&#xff0c;“仍要安装”。但我这次遇到的问题是&#xff0c;根本没有这个选项&#xff0c;只有“应用市场”和“取消”&…

数据结构:二叉树的遍历和线索二叉树

二叉树的遍历 二叉树的遍历是二叉树的一种重要的操作&#xff0c;指按照某种顺序访问树中的每个节点&#xff0c;并且每个节点仅被访问一次。常见的遍历方式有四种&#xff1a;前序遍历、中序遍历、后序遍历和层次遍历&#xff08;或称为广度优先遍历&#xff09;。 二叉树的…

物联网系统中LCD屏主流驱动方案详解

01 物联网系统中为什么要使用LCD驱动芯片 在物联网系统中使用LCD驱动芯片的原因主要有以下几点&#xff1a; 节省资源 1、减少IO端口占用&#xff1a;在物联网设备中&#xff0c;单片机或其他主控芯片的IO资源通常非常有限。LCD驱动芯片可以通过简单的接口&#xff08;如SP…

基于Hive和Hadoop的白酒分析系统

本项目是一个基于大数据技术的白酒分析系统&#xff0c;旨在为用户提供全面的白酒市场信息和深入的价格分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark 为核…

jenkins项目发布基础

随着软件开发需求及复杂度的不断提高,团队开发成员之间如何更好地协同工作以确保软件开发的质量已经慢慢成为开发过程中不可回避的问题。Jenkins 自动化部署可以解决集成、测试、部署等重复性的工作,工具集成的效率明显高于人工操作;并且持续集成可以更早的获取代码变更的信息,…

从Linux系统的角度看待文件-基础IO

目录 从Linux系统的角度看待文件 系统文件I/O open write read 文件操作的本质 vim中批量注释的方法 从Linux系统的角度看待文件 关于文件的共识&#xff1a; 1.空文件也要占用磁盘空间 2.文件内容属性 3.文件操作包括文件内容/文件属性/文件内容属性 4.文件路径文…

【Qt】前后端交互---DataCenter类

设计目的 前后端交互系统中&#xff0c;创建并使用数据核心类的目的就是让该类作为客户端的数据中心&#xff0c;也就是说其负责管理客户端的所有数据与服务器的网络通信。 数据持久化 初始化数据文件 该函数设计的目的就是用于检查所需要的文件和目录是否存在&#xff0c;如…

短视频矩阵系统源码开发/矩阵系统OEM搭建--源代码开发经验分享

短视频矩阵系统开发策略 短视频矩阵系统源码的原生开发方法 一、基于原生技术的短视频矩阵系统开发途径 原生编程语言&#xff1a;采用各平台专有的编程语言及开发工具&#xff0c;如iOS平台的Swift或Objective-C&#xff0c;以及平台的Java或Kotlin&#xff0c;确保应用性能与…

[贪心+数学/数学+位运算] 两种方法O(1)解决 消减整数

标题&#xff1a;[贪心数学/数学位运算] 两种方法O(1)解决 消减整数 个人主页水墨不写bug 目录 一、题目&#xff1a;消减整数(Newcoder) 二、题目分析 1.理解题意&#xff1a; 2.解决问题 解法详解一&#xff1a;贪心数学 解法一参考代码&#xff1a; 解法详解二&#xf…

WiFi无线连接管理安卓设备工具:WiFiADB

介绍 WiFi ADB 使您能够通过 WiFi TCP/IP 连接直接在设备上轻松调试和测试 Android 应用&#xff0c;无需使用 USB 数据线。在启用 WiFi 上的 ADB 后&#xff0c;打开控制台将电脑连接到设备。 手机和电脑在同一个WiFi然后电脑上运行adb connect x.x.x.x:x命令即可 下载 谷…

MindSearch 部署到Github Codespace 和 Hugging Face Space

和原有的CPU版本相比区别是把internstudio换成了github codespace。 教程是https://github.com/InternLM/Tutorial/blob/camp3/docs/L2/MindSearch/readme_github.md 复现步骤&#xff1a; 根据教材安装环境和创建硅基流动 API 然后启动前后端 然后按照教材部署到 Huggi…

一站式家装服务管理系统

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本一站式家装服务管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数…

基于Hive和Hadoop的病例分析系统

本项目是一个基于大数据技术的医疗病历分析系统&#xff0c;旨在为用户提供全面的病历信息和深入的医疗数据分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…

《征服数据结构》哈夫曼树(Huffman Tree)

摘要&#xff1a; 1&#xff0c;哈夫曼树的介绍 2&#xff0c;哈夫曼树的构造 3&#xff0c;哈夫曼树带权路径长度计算 4&#xff0c;哈夫曼树的编码 5&#xff0c;哈夫曼树的解码 1&#xff0c;哈夫曼树的介绍 哈夫曼树(Huffman Tree)也叫霍夫曼树&#xff0c;或者赫夫曼树&am…

学校周赛(1)

A - Short Sort 题目&#xff1a; 思路&#xff1a; 本条题目只允许改一处地方&#xff0c;只有三个字母&#xff0c;我们可以直接枚举所有移动过的结果&#xff0c;同时使用哈希去记录其值&#xff0c;对于每一个输入我们都寻找是否有这个值记录&#xff0c;有则输出YES否则…

微深节能 环形运动机械定位控制系统 格雷母线

微深节能的环形运动机械定位控制系统中的格雷母线&#xff0c;是一种高精度、无磨损的非接触式位置检测系统&#xff0c;特别适用于环形运动机械的定位控制。该系统主要由格雷母线、天线箱、电气柜等关键部件组成&#xff0c;其核心在于格雷母线这一特殊的编码线。 格雷母线概述…

JAVA一站式台球学习平台多端畅享助教教练系统小程序源码

​一站式台球学习平台 —— 多端畅享助教教练系统 &#x1f31f;【开篇&#xff1a;解锁台球新境界】&#x1f31f; 你是否厌倦了传统台球学习的枯燥与局限&#xff1f;想要随时随地&#xff0c;都能享受专业级的台球指导吗&#xff1f;今天&#xff0c;就让我为你揭秘一款颠覆…

JITWatch安装使用方法

JITWatch 版本1.4.2 JDK 版本 11以上 1.下载JITWatch&#xff1a; https://github.com/AdoptOpenJDK/jitwatch/releases/download/1.4.2/jitwatch-ui-1.4.2-shaded-win.jar 2.启动 bat脚本执行&#xff1a;通过启动jar包方式启动JITWatch echo off start cmd /c "ti…

SpringBoot+Activiti7工作流入门实例

目录 文章目录 目录准备Activiti建模工具1、BPMN-js在线设计器1.1 安装1.2 使用说明1.3运行截图 2、IDEA安装Activiti Designer插件2.1安装插件2.2 设置编码格式防止中文乱码2.3 截图 简单工作流入门实例1. 新建Spring Boot工程2. 引入Activiti相关依赖添加版本属性指定仓库添加…

探索分布式IO模块的介质冗余:赋能工业自动化的稳健之心

在日新月异的工业自动化领域&#xff0c;每一个细微环节的稳定性都直接关系到生产线的效率与安全。随着智能制造的深入发展&#xff0c;分布式IO&#xff08;Input/Output&#xff09;模块作为连接现场设备与控制系统的关键桥梁&#xff0c;其重要性日益凸显。我们自主研发的带…