Go —— defer

defer

defer 语句用于延迟函数的调用,常用于关闭文件描述符、释放锁等资源释放场景。但 defer 关键字只能作用于函数或函数调用。

defer func(){						// 函数
    fmt.Print("Hello,World!")
}()

defer fmt.Print("Hello,World!")		// 函数调用

1. 执行机制

1.1 执行时机

在Go语言的函数中 return 语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer 语句执行的实际就在返回值操作后,RET指令前。具体如下图所示:

defer执行时机

1.2 行为规则

1)规则一:延迟函数的参数在 defer 语句出现时就已经确定了

示例如下:

func a() {
    i := 0
    defer fmt.Println(i)	// 程序运行打印 0
    i++
    return
}

defer 语句中的 fmt.Println() 参数 i 值 在 defer 出现时就已经确定了,实际上是复制了一份。后面对变量 i 的修改不会影响 fmt.Println() 函数的调用,依旧打印 0。

对于指针类型参数,此规则依然适用,只不过延迟函数的参数是一个地址值,在这种情况下,defer 后面的语句对变量的修改可能会影响延迟函数。

2)规则二:延迟函数按照后进先出 的顺序执行

设计 defer 的初衷是简化函数返回时资源清理的动作,资源往往有依赖顺序,比如申请资源的顺序时 A→B→C,释放的顺序往往又要反向进行。这就是把 defer 设计成 LIFO 的原因

3)规则三:延迟函数可能操作主函数的具名返回值

定义 defer 的函数(下称主函数)可能有返回值,返回值可能有名字(具名返回值),也可能没有返回值(匿名返回值),延迟函数可能会影响返回值。

举个栗子:

func deferFuncReturn() (result int){
    i := 1
    
    defer func() {
        result++
    }()
    return i	// 程序返回 2
}

上面已经介绍过了 defer 的执行时机,该函数的 return 语句可以拆分成下面三行:

	result = i
	result++
	return

主函数有不同的返回方式,包括匿名返回值和具名返回值,但万变不离其宗,只要把 return 语句拆开都可以很好理解,下面分别举例说明:

(1)主函数拥有匿名返回值,返回字面值

一个主函数拥有一个匿名返回值,返回时使用字面值,这种情况下 defer 语句时无法操作返回值的

func foo() int {
	var i int
	
	defer func() {
		i++
	)()

	return 1
}

上面的 return 语句直接把 1 写入栈中作为返回值,延迟函数无法操作该返回值

(2)主函数拥有匿名返回值,返回变量

一个主函数拥有一个匿名返回值,返回本地或全局变量,这种情况下 defer 语句可以引用返回值,反不会改变返回值

func foo() int {
	var i int
	
	defer func(){
		i++
	}
	return i
}

假定返回值变量为 anony ,上面的返回语句可以拆分为以下过程:

	annnoy = i
	i++
	return

函数返回 0

(3)主函数拥有具名返回值

主函数声明语句中带名字的返回值会被初始化为一个局部变量,函数内部可以像使用局部变量一样使用该返回值。如果 defer 语句操作该返回值,则可能改变返回结果。

一个影响函数返回值的例子:

func foo() (ret int) {
	defer func() {
		ret++
	}()
	
	return 0     

上面的函数拆解出来如下所示:

	ret = 0
	ret++
	return

函数真正返回前,在 defer 中对返回值做了 +1 操作,所以函数最终返回 1

2. 实现原理

2.1 数据结构

源码包中 src/src/runtime/runtime2.go:_defer 定义了 defer 的数据结构

type _defer struct {
	...
	sp        uintptr // 函数栈指针
	pc        uintptr // 程序计数器
	fn        func()  // 函数地址
	link      *_defer // 指向自身结构的指针,用于链接多个 defer
	...
}

编译器会把每个延迟函数编译成一个 _defer 实例暂存到 goroutine 数据结构中,待函数结束时再逐个取出执行。
每个defer 语句对应一个 _defer 实例,多个实例使用指针 link 链接起来形成一个单链表,保存到 goroutine 数据结构中。
goroutine 的数据结构如下所示:

type g struct {
	...
	_defer *_defer // defer 链表
	...
}

每次插入 _defer 实例时均插入链表头部,函数执行结束时再依次从头部取出,从而实现后进先出的效果。
一个 goroutine 可能连续调用多个函数,defer 的添加过程跟上述流程一致,进入函数时添加 defer ,离开函数时取出 defer ,所以即便调用多个函数,也总是能保证 defer 是按 LIFO 方式执行的。

3. 小结

  • defer 定义的延迟函数参数在 defer 语句出现时就已经确定了
  • defer 定义的顺序与实际地执行顺序相反
  • return 不是原子操作,执行过程是:保存返回值 → 执行 defer → 执行 ret 跳转
  • 申请资源后立即使用 defer 关闭资源是一个好习惯

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在CentOS安装可视化Docker容器管理工具Portainer并无公网IP远程管理

文章目录 前言1. 部署Portainer2. 本地访问Portainer3. Linux 安装cpolar4. 配置Portainer 公网访问地址5. 公网远程访问Portainer6. 固定Portainer公网地址 前言 Portainer 是一个轻量级的容器管理工具,可以通过 Web 界面对 Docker 容器进行管理和监控。它提供了可…

智慧公厕,为智慧城市建设注入了新的活力

随着智慧城市的快速发展,公共厕所不再是简单的功能设施,而是成为了提升城市形象、改善民生服务的重要一环。智慧公厕作为新形态的公共厕所,通过精准监测公厕内部的人体活动状态、人体存在状态、空气质量情况、环境变化情况、设施设备运行状态…

Occupancy 后处理

文章目录 bev坐标与自车坐标转换如何创建旋转矩阵 (R_veh) 偏航3D Voxel -> 2D Grid 在进行占据空间(occupancy)后处理时,需要将不同感知模块的输出进行综合融合,以实现更精确的空间占据和环境感知。以下是针对您提到的几个方面…

Java代码基础算法练习-求一个三位数的各位数字之和-2024.03.27

任务描述&#xff1a; 输入一个正整数n&#xff08;取值范围&#xff1a;100<n<1000&#xff09;&#xff0c;然后输出每位数字之和 任务要求&#xff1a; 代码示例&#xff1a; package M0317_0331;import java.util.Scanner;public class m240327 {public static voi…

LangChain入门:2.OpenAPI调用ChatGPT模型

引言 在本文中&#xff0c;我们将带您深入探索如何通过OpenAPI与ChatGPT模型进行高效交互&#xff0c;实现智能文本问答功能。通过LangChain库的实践&#xff0c;您将学习构建一个能够与用户进行自然语言对话的系统的关键步骤。 准备步骤 在动手编码之前&#xff0c;请确保您…

Linux之进程控制进程终止进程等待进程的程序替换替换函数实现简易shell

文章目录 一、进程创建1.1 fork的使用 二、进程终止2.1 终止是在做什么&#xff1f;2.2 终止的3种情况&&退出码的理解2.3 进程常见退出方法 三、进程等待3.1 为什么要进行进程等待&#xff1f;3.2 取子进程退出信息status3.3 宏WIFEXITED和WEXITSTATUS&#xff08;获取…

从零开始的软件开发实战:互联网医院APP搭建详解

今天&#xff0c;笔者将以“从零开始的软件开发实战&#xff1a;互联网医院APP搭建详解”为主题&#xff0c;深入探讨互联网医院APP的开发过程和关键技术。 第一步&#xff1a;需求分析和规划 互联网医院APP的主要功能包括在线挂号、医生预约、医疗咨询、健康档案管理等。我们…

2024河北煤炭装备展览会|河北采煤装备展会|河北煤博会

2024中国&#xff08;石家庄&#xff09;国际煤炭装备及矿山设备博览会 时间&#xff1a;2024年7月4-6日 地点&#xff1a;石家庄国际会展中心.正定在快速发展的科技时代&#xff0c;能源行业始终是国家发展的重要支柱。作为传统的能源巨头&#xff0c;煤炭产业在面临转型升…

jdk8中的Arrays.sort

jdk8中Arrays.sort 这里可以看到根据传入数组类型的不同&#xff0c;排序的算法是由区别的。 拆分解析 我们在平时引用的时候&#xff0c;一般只会传入一个数组&#xff0c;但是真正调用的时候&#xff0c;参数会进行补全。 public static void sort(int[] a) {DualPivotQui…

获奖喜讯 | 思迈特软件蝉联双奖,品牌及产品实力再获认可

近期&#xff0c;思迈特软件又传来获奖捷报&#xff0c;凭借出色的产品力及品牌实力&#xff0c;思迈特软件Smartbi一站式大数据分析平台荣登2023ToB头条影响力价值榜“创新力产品TOP50”榜单&#xff0c;又获广东省云计算应用协会“2023年度大数据创新企业奖”。 荣登“ToB行业…

贪心算法--最大数

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 本题链接https://leetcode.cn/problems/largest-number/description/ class Solution { public:bool static compare(int a, int b){return (to_string(a) to_string(b)) > (to_string(b) to_string(a));}bool operato…

爱思助手验机不靠谱?

1.骗子只能骗的一种人就是有点懂 因为完全不懂的不会感兴趣 骗不到 太懂的人 基本属于猴精的人 你骗不到 2. 3.基本做的是翻新机 维修过的 4。转载 爱思助手验机不靠谱&#xff1f;“报告全绿”已成奸商的阴谋 - 知乎

Windows无法安装torch==1.4.0

在conda中&#xff0c;每创建一个虚拟环境&#xff0c;就要重新配置其中的pytorch 这次我创建的虚拟环境需要torch1.4.0的版本。 torch网址&#xff1a;https://pytorch.org/get-started/previous-versions/ 解决办法 按以下代码进行安装&#xff1a; pip install torch0.4.0…

短视频账号矩阵系统/开发 -- -- -- 蒙太奇算法上线

短视频账号矩阵系统&#xff0c;短视频矩阵系统开发3年技术之路&#xff0c;目前已经在技术竞品出沉淀出来&#xff0c;近期技术迭代的新的功能同步喽&#xff1a; php7.4版本&#xff0c;自研框架&#xff0c;有开发文档&#xff0c;类laravel框架 近期剪辑迭代的技术算法&am…

【Pytorch入门】小土堆PyTorch入门教程完整学习笔记(详细笔记并附练习代码 ipynb文件)

小土堆PyTorch入门教程笔记 最近在观看PyTorch深度学习快速入门教程&#xff08;绝对通俗易懂&#xff01;&#xff09;【小土堆】顺便做点笔记&#xff0c;方便回看&#xff0c;同时也希望记录的笔记能够帮助到更多在入门的小伙伴~ 【注】仅记录个人觉得重要的知识&#xff0c…

【米粉福音】小米SU7引领智能汽车新时代

2024年3月28日&#xff0c;小米公司正式发布旗下全新智能汽车产品——小米SU7。这一发布不仅是小米品牌向汽车领域的重大跨界进军&#xff0c;更是对智能科技与汽车行业融合发展的一次里程碑式的尝试。 小米SU7的发布&#xff0c;意味着小米公司与合作伙伴达成的三年之约的成功…

python--字符串和常见的方法

1.字符串对象 字符串 " 字符串 " """ 字符串 """ 字符串 str() #全局函数&#xff0c;将一个类型转化为字符串 len(字符串) #获取字符串长度 while 和 for 循环&#xff0c;遍历字符串 案例一&#xff1a;查看字…

Java开发过程中如何进行进制换换

最近由于工作上的需要&#xff0c;遇到进制转换的问题。涉及到的进制主要是十进制、十六进制、二进制转换。 1、十进制转十六进制、二进制 调用java自带的api,测试十进制转16进制、2进制 package com.kangning.common.utils.reflect;/*** 十进制 转 十六进制* 十进制 转 二进…

蓝牙耳机哪个品牌的好?2024年精选硬核机型推荐

​随着时代的进步和潮流的演进&#xff0c;人们对蓝牙耳机的需求已不再局限于音质&#xff0c;舒适度也成为了关键考量。下面&#xff0c;我将为你推荐五款既舒适又性能出色的蓝牙耳机。 一、如何挑选蓝牙耳机&#xff1f;&#xff08;重点码住&#xff09; 1.选择知名大品牌&…

Win10或Win11系统下西门子TIA博途运行时卡顿缓慢的解决办法总结

Win10或Win11系统下西门子TIA博途运行时卡顿缓慢的解决办法总结 首先,可以看下TIA PORTAL V19的安装条件: 处理器:Intel i5-8400H,2.5-4.2GHZ,4核以上+超线程技术,智能缓存; 内存:至少16GB,大型项目需要32GB 硬盘:必须SSD固态硬盘,至少50GB的可用空间 图形分辨率:1…