defer学习指南

一、源头:

早期管理资源(如数据库连接、锁、文件句柄、网络连接)和状态清理异常麻烦。
必须在每个可能的返回点(return、err、panic)手动重复清理代码,极易遗漏且打断主要逻辑思路!

像Java语言虽然用了Try-Catch,但缺点是逻辑不清晰、臃肿、不容易判断错误出在什么地方。

作为新生代的Go,及众多语言之精华,推出了defer处理机制。
尤其是在Go1.14版本时,性能开销接近零,更无后顾之忧。

二、定义:

1、语法:

defer functionCall(..arguments..)

defer后面直接跟一个函数调用(可以是命名函数/匿名函数/方法...)

2、定义:

当函数执行到defer语句时(注册时),他会立即求值此时该函数调用的参数并将此次函数调用(包括已求值的参数)放到一个延迟调用表中。这个调用函数与goroutine关联,采用LIFO(后进先出的方式调用)。切记这个延迟调用表不会立即执行,而是会等到(函数真正结束之--函数(return或panic)之)在调用。

3、特性:

1、延迟执行:运行到defer时,只是将求值后的参数与调用的函数一并打包到延迟调用表中,需等到函数体结束之后在执行

2、LIFO方式执行:后进先出的方式执行

3、参数求值时机:defer 语句中的函数参数的值,是在执行到defer语句时(即注册时)就确定并保存下来的,而不是在延迟函数实际执行时才求值。

4、作用域:自各函数体

三、应用:

1、临近释放:(逻辑清晰)

mu.Lock()
defer mu.Unlock() // 好习惯!确保解锁
// ... 操作共享数据 ...

2、panic补获:(防止程序崩溃)

特殊情况,根据源码分析---协程中出现panic,若不能再该协程中捕获,则会导致整个程序崩溃。

func test(){defer func(){if r := recover(); r!=nil{fmt.Println(r);}}()panic(1);
}

3、循环函数释放:(利用完资源后,及时释放资源)

// 正确做法:将文件处理封装到函数,defer 在每次循环的匿名函数结束时执行
func outerFunc() {for _, filename := range filenames {func() { // 匿名函数f, err := os.Open(filename)if err != nil {log.Println(err)return // 退出匿名函数}defer f.Close() // 延迟到当前匿名函数结束时执行 (即本次循环结束)// ... 处理 f ...}() // 立即调用匿名函数}
}

4、查看执行顺序:

代码右上角,有个运行小按钮,点击运行查看。

package mainimport ("fmt""log"
)func g(i int) {if i > 1 {fmt.Println("Panicking!")panic(1)}defer fmt.Println("Defer in g", i)fmt.Println("Printing in g", i)g(i + 1)
}func f() {defer func() {if r := recover(); r != nil {log.Println("Recovered in f", r)}}()fmt.Println("Calling g.")g(0)fmt.Println("Returned normally from f.")
}func main() {f()
}

四、底层:

这个是我扒出来的底层源码,重点了解heap、link这俩。

type _defer struct {heap      bool  	//表示是分配在堆上还是栈上。rangefunc bool    	// true for rangefunc listsp        uintptr 	// 栈指针pc        uintptr 	// 程序计数器fn        func()  	// 表示需要被延迟执行的函数。link      *_defer 	// 指向下一个 _defer 结构体的指针。// If rangefunc is true, *head is the head of the atomic linked list// during a range-over-func execution.head *atomic.Pointer[_defer]
}
  • defer 语句注册时,会创建一个 _defer 结构体实例。

  • 多个 defer 通过 link 字段形成一个单链表(LIFO 栈),挂载到当前 goroutine 的结构上(g._defer)。新的 defer 总是插入链表头部。

主要有两大种分配方式。

1、堆栈分配

区别:分配位置的不同

获取到runtime_defer结构体,它都会被追加到所在 Goroutine _defer 链表的最前面。

2、开放编码

不建额外结构,直接把 defer 代码塞到函数退出前,用位掩码控制执行,开销几乎和普通调用一样。

3、选择:

首先考虑开放编码(已经优化到:实际消耗跟调用普通函数差不多的地步),后栈分配保底堆分配

以下是整理的Go版本迭代全史,有兴趣的可以一看,挺有趣的。


📅 Go 版本迭代全史(2009–2025)

⭐ 早期阶段

版本发布时间核心特性
初始开源2009-11-10正式开源,获得 TIOBE 年度语言称号
Go r562011-03-16首个稳定版本
Go 1.02012-03-28首个正式版本,承诺向后兼容性;引入 go tool pprofgo vet

🔄 每半年发布周期(2013 年起)

版本发布时间核心特性
Go 1.12013-05-13重写调度器(支持 Work-Stealing 算法);引入竞态检测器
Go 1.22013-12-01支持全切片表达式;go test 支持覆盖率统计
Go 1.32014-06-18栈模型改为连续栈;引入 sync.Pool
Go 1.42014-12-10支持 Android;运行时从 C 改为 Go;移除 src/pkg 层级
Go 1.52015-08-19自举(移除 C 代码);优化 GC(延迟降至 30ms);引入 vendor 机制
Go 1.62016-02-17默认支持 HTTP/2;GC 延迟进一步降低
Go 1.72016-08-15引入 context 包;SSA 后端优化(性能提升 5–35%)
Go 1.82017-02-17GC 延迟降至亚毫秒级;defer 性能提升 50%
Go 1.92017-08-24引入类型别名;新增并发安全的 sync.Map
Go 1.102018-02-16构建缓存(Build Cache)默认开启
Go 1.112018-08-25引入 Go Modules;支持 WebAssembly
Go 1.122019-03-01优化 TLS 1.3 支持;改进模块机制
Go 1.132019-09-03支持二进制/八进制字面量;错误处理增强(errors.Is/As/Unwrap
Go 1.142020-02-25接口允许方法集重叠;Goroutine 支持异步抢占调度;defer 性能接近零开销
Go 1.152020-08-11优化链接器;改进内联策略
Go 1.162021-02支持静态文件嵌入;默认启用 Go Modules

🚀 重大革新阶段(2022–2025)

版本发布时间核心特性
Go 1.182022-03-15引入泛型;支持模糊测试(Fuzzing);工作区模式(Multi-Module Workspaces)
Go 1.232025 年初引入迭代器(seq/seq2);gopls 现代化工具链;go get 管理工具链
Go 1.242025 年中标准库支持 strings/slices/maps 迭代器;增强 WebAssembly 安全性与性能
Go 1.252025-08 (预计)移除核心类型(Core Types);简化泛型规范;优化错误提示

太多了不好记,有兴趣查看时,可以重点看

1、初始开源-2009-11-10,go降生到了这个世界上

2、Go1.5 :2015,go开始用母语了,实现自举(Go 编译 Go),GC 延迟从 300ms 降至 30ms,奠定现代 Go 基础

3、Go1.11:2018,引入了Go Module解决了依赖问题,让现在的我都收益不止--今年2025

4、Go1.18:2022,泛型、模糊测试、工作区多模块,等均进行了新功能的填充与优化。这个咱暂接触不够多,后期接触了,会回来优化本篇博客

.....

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NLP_知识图谱_大模型——个人学习记录

1. 自然语言处理、知识图谱、对话系统三大技术研究与应用 https://github.com/lihanghang/NLP-Knowledge-Graph 深度学习-自然语言处理(NLP)-知识图谱:知识图谱构建流程【本体构建、知识抽取(实体抽取、 关系抽取、属性抽取)、知识表示、知…

linux:进程详解(1)

目录 ​编辑 1.进程基本概念与基本操作 1.1 概念 1.2 描述进程-PCB 1.2.1PCB的基本概念 1.2.2 task_ struct 1.2.3 查看进程 2.进程状态 2.1 Linux内核源码展示 2.2 进程状态查看 ​编辑 2.3 Z(zombie)-僵⼫进程 2.4 僵尸进程的危害 2.5 孤儿进程 3.进程优先级 …

碳中和目标下的全球产业链重构:深度解析与未来路径

引言:气候临界点与产业链的系统性风险2023年,全球平均气温较工业化前上升1.2℃,南极冰盖年消融量达1500亿吨,极端天气事件导致的经济损失占全球GDP的2.3%。这一系列数据背后,暴露出传统产业链的致命缺陷——其设计逻辑…

FPGA实现SDI转LVDS视频发送,基于GTX+OSERDES2原语架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目本博已有的 SDI 编解码方案FPGA实现LVDS视频收发方案 3、工程详细设计方案工程设计原理框图SDI 输入设备Gv8601a 均衡器GTX 解串SMPTE SD/HD/3G SDI IP核BT1120转RGB奇…

新手向:使用Python构建高效的日志处理系统

本文将详细讲解如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率。环境准备开发本工具需要以下环境配置:Python环境:建议Python 3.8或更高版本必要库:pandas&…

大模型-量化技术

简介 模型量化是一种重要的模型压缩技术。其核心目标是在可控精度损失下,将大模型中浮点型权重(通常为 float32 等高精度格式)近似转换为低精度离散值表示(通常为 int8)。 具体而言,该技术通过将模型的权重…

【C语言网络编程】HTTP 客户端请求(域名解析过程)

在做 C 语言网络编程或模拟 HTTP 客户端时,第一步就离不开“把域名解析为 IP 地址”这一步。很多人可能直接复制粘贴一段 gethostbyname 的代码,但未必真正理解它的原理。 本篇博客将围绕一个经典函数: char *host_to_ip(const char *hostna…

Node.js特训专栏-实战进阶:16. RBAC权限模型设计

🔥 欢迎来到 Node.js 实战专栏!在这里,每一行代码都是解锁高性能应用的钥匙,让我们一起开启 Node.js 的奇妙开发之旅! Node.js 特训专栏主页 专栏内容规划详情 我将从RBAC权限模型的基础概念、核心组件讲起,详细阐述其设计原则、数据库模型设计,还会结合代码示例展示在…

mac上BRPC的CMakeLists.txt优化:解决Protobuf路径问题

问题背景与挑战 在构建高性能RPC框架BRPC时,​Protobuf依赖路径的配置往往是开发者面临的主要挑战之一。原始CMake配置在寻找Protobuf库时存在以下痛点: ​路径搜索不精确​:默认find_library无法定位自定义安装路径下的Protobuf​版本兼容…

Go 性能分析利器:pprof 工具实战指南

在 Go 语言开发中,性能问题往往是项目上线后最棘手的挑战之一。无论是 CPU 占用过高、内存泄漏,还是 goroutine 失控,都可能导致服务响应缓慢甚至崩溃。而pprof作为 Go 官方提供的性能分析工具,就像一把精准的手术刀,能…

fio测试SSD直接I/O(Direct IO)性能仅有100MB/s的问题解决

针对您使用fio测试SSD直接I/O(Direct IO)性能仅有100MB/s的问题,结合SSD特性和fio测试原理 fio测试SSD直接I/O(Direct IO)性能仅有100MB/s的问题 - LinuxGuideLinuxGuide 以下是可能的原因及优化方案: &a…

EVO-0:具有隐空间理解的视觉-语言-动作模型

25年6月来自上海交大、EvoMind Tech 和上海算法创新研究院(IAAR-Shanghai)的论文“EVO-0: Vision-Language-Action Model with Implicit Spatial Understanding”。 视觉-语言-动作 (VLA) 模型已成为一种有前途的框架,可使通用机器人能够在现…

文心大模型4.5开源测评:轻量化部署实践与多维度能力验证

前言:开源浪潮下的轻量化革命 2025年百度文心大模型4.5系列的开源,标志着国产大模型从“参数竞赛”转向“实用落地”的关键转折。当行业仍在追逐千亿参数模型时,文心4.5以0.3B轻量级模型撕开一条新赛道——单卡部署、低成本运维、中文场景高…

LeetCode 2401.最长优雅子数组

给你一个由 正 整数组成的数组 nums 。 如果 nums 的子数组中位于 不同 位置的每对元素按位 与(AND)运算的结果等于 0 ,则称该子数组为 优雅 子数组。 返回 最长 的优雅子数组的长度。 子数组 是数组中的一个 连续 部分。 注意:长…

中华心法问答系统的解读(1)

中华心法问答系统一、研究背景1. 研究意义2. 研究目的3. 信息检索技术二、主要研究内容三、相关技术介绍1. Flask框架技术2. BERT模型(1)基本概念(2)BERT解决的问题(3)BERT的核心结构a. 模型结构b. 预训练任…

Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频摘要快速生成与检索优化(345)

Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频摘要快速生成与检索优化(345)引言:正文:一、Java 构建的全场景视频处理系统(含校园 / 工厂 / 矿区适配)1.1 校园宿舍区夜间检索方案(…

信号量机制,互斥的避免自旋锁的实现方法(操作系统)

这次的比喻场景要升级了,因为它既能解决互斥问题,也能解决同步问题。我们用一个更综合的场景:一个拥有多辆共享单车的站点。共享单车 (资源):站点里有多辆共享单车,数量是有限的。你 (进程):想借一辆车去办…

零基础 “入坑” Java--- 十、继承

文章目录一、何为继承二、继承语法三、父类成员访问1.成员变量2.成员方法四、super关键字五、子类构造方法六、super和this辨析七、再谈初始化八、protected关键字九、继承方式十、final关键字十一、继承与组合根据我们学过的类的知识,我们来定义两个类:…

JS进阶-day1 作用域解构箭头函数

作用域全局作用域——>尽量少使用,避免变量污染局部作用域——>函数作用域、块级作用域作用域链——>底层变量查找机制(先在当前函数作用域查找,如果找不到,就沿着作用域链向上级作用域查找,直到全局作用域&a…

Arduino 无线通信实战:使用 RadioHead实现 315MHz 433M模块数据传输

本文将介绍如何使用 Arduino 和 RadioHead 库实现 315MHz(或 433MHz)ASK 无线通信。通过两个 Arduino 控制板,一个作为发射端,一个作为接收端,实现“按键控制 → 无线发送 → LED 控制”的基础通信功能,非常…