Golang - defer关键字 深入剖析
defer关键字
defer和go一样都是Go语言提供的关键字。defer用于资源的释放,会在函数返回之前进行调用。一般采用如下模式:
f,err := os.Open(filename)
if err != nil {panic(err)
}
defer f.Close()
如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用。
不过如果对defer的了解不够深入,使用起来可能会踩到一些坑,尤其是跟带命名的返回参数一起使用时。在讲解defer的实现之前先看一看使用defer容易遇到的问题。
defer使用时的坑
先来看看几个例子。例1:
func f() (result int) {defer func() {result++}()return 0
}
例2:
func f() (r int) {t := 5defer func() {t = t + 5}()return t
}
例3:
func f() (r int) {defer func(r int) {r = r + 5}(r)return 1
}
请读者先不要运行代码,在心里跑一遍结果,然后去验证。
例1的正确答案不是0,例2的正确答案不是10,如果例3的正确答案不是6......
defer是在return之前执行的。这个在 官方文档中是明确说明了的。要使用defer时不踩坑,最重要的一点就是要明白,return xxx这一条语句并不是一条原子指令!
函数返回的过程是这样的:
先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。
defer表达式可能会在设置函数返回值之后,在返回到调用函数之前,修改返回值,使最终的函数返回值与你想象的不一致。
其实使用defer时,用一个简单的转换规则改写一下,就不会迷糊了。改写规则是将return语句拆成两句写,return xxx会被改写成:
返回值 = xxx
调用defer函数
空的return
先看例1,它可以改写成这样:
func f() (result int) {result = 0 //return语句不是一条原子调用,return xxx其实是赋值+ret指令func() { //defer被插入到return之前执行,也就是赋返回值和ret指令之间result++}()return
}
所以这个返回值是1。
再看例2,它可以改写成这样:
func f() (r int) {t := 5r = t //赋值指令func() { //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过t = t + 5}return //空的return指令
}
所以这个的结果是5。
最后看例3,它改写后变成:
func f() (r int) {r = 1 //给返回值赋值func(r int) { //这里改的r是传值传进去的r,不会改变要返回的那个r值r = r + 5}(r)return //空的return
}
所以这个例子的结果是1。
defer确实是在return之前调用的。但表现形式上却可能不像。本质原因是return xxx语句并不是一条原子指令,defer被插入到了赋值 与 ret之间,因此可能有机会改变最终的返回值。
defer的实现
defer关键字的实现跟go关键字很类似,不同的是它调用的是runtime.deferproc而不是runtime.newproc。
在defer出现的地方,插入了指令call runtime.deferproc,然后在函数返回之前的地方,插入指令call runtime.deferreturn。
普通的函数返回时,汇编代码类似:
add xx SP
return
如果其中包含了defer语句,则汇编代码是:
call runtime.deferreturn,
add xx SP
return
goroutine的控制结构中,有一张表记录defer,调用runtime.deferproc时会将需要defer的表达式记录在表中,而在调用runtime.deferreturn的时候,则会依次从defer表中出栈并执行。
相关文章:
Golang - defer关键字 深入剖析
defer关键字 defer和go一样都是Go语言提供的关键字。defer用于资源的释放,会在函数返回之前进行调用。一般采用如下模式: f,err : os.Open(filename) if err ! nil {panic(err) } defer f.Close()如果有多个defer表达式,调用顺序类似于栈&a…...
如何在Spring Boot中使用@Scheduled写定时任务判断数据量是否过大,过大则进行分表操作,多张表使用临时视图查询
当数据量过大,在定时任务中执行分表操作 1、复制表结构及数据 在xml中编写复制表结构及数据(newTableName为新表名、originalTableName为原始表名) 只复制表结构: CREATE TABLE ${newTableName} AS SELECT * FROM ${originalTa…...
使用jieba库进行中文分词和去除停用词
jieba.lcut jieba.lcut()和jieba.lcut_for_search()是jieba库中的两个分词函数,它们的功能和参数略有不同。 jieba.lcut()方法接受三个参数:需要分词的字符串,是否使用全模式(默认为False)以及是否使用HMM模型&…...
C语言之分支与循环【附6个练习】
文章目录 前言一、什么是语句?1.1 表达式语句1.2 函数调用语句1.3 控制语句1.4 复合语句1.5 空语句 二、分支语句(选择结构)2.1 if语句2.1.1 悬空else2.1.2 练习(1. 判断一个数是否为奇数 2. 输出1-100之间的奇数) 2.2…...
使用通用MCU实现无人机飞行任务的快速二次开发
使用通用MCU实现无人机飞行任务的快速二次开发 ---TIDronePilot外部控制offboard模式介绍 无名小哥 2024年1月1日 传统飞控二次开发方法和主要存在的问题简介 通过对前面几讲中《零基础竞赛无人机积木式编程指南》系列开发教程的学习可知,在以往TI电赛真题的学习…...
什么是Selinux
官网地址:What is SELinux? 欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯. 概述 安全增强型 Linux (SELinux) 是Linux 系统的安全架构,允许管理员更好地控制谁可以访问系统。它最初是由美…...
计算机网络知识点
1. URI 和 URL 统一资源定位符(Uniform Resource Locator,缩写:URL),是对资源的引用和访问该资源的方法。俗称网址,就是浏览器地址栏里面的内容。 URL 语法为:protocol://userInfohost:port/p…...
Qt 连接 Mysql
Linux下安装mysql及qt连接_liunx下安装mysql及qt链接-CSDN博客...
HarmonyOS4.0系统性深入开发14AbilityStage组件容器
AbilityStage组件容器 AbilityStage是一个Module级别的组件容器,应用的HAP在首次加载时会创建一个AbilityStage实例,可以对该Module进行初始化等操作。 AbilityStage与Module一一对应,即一个Module拥有一个AbilityStage。 DevEco Studio默…...
客服系统接入FastGPT
接入FastGPT 点击【应用】【外部使用】【API访问】【新建】新建一个KEY,同时也可以看到我们的API根地址 这个根地址和Key可以填入任何支持OpenAI接口的应用里,这个接口是兼容OpenAI格式。 在客服系统【知识库AI配置】里填上接口地址和接口密钥。这样我…...
Hi5 2.0 虚拟手与追踪器(Tracker)的位置修正
问题描述 使用环境与工具:Unity 2022.3.4fc1,steam VR(2.7.3),steamvrSDK(1.14.15),HTC vive pro专业版,Hi5 2.0数据手套 首先按照Hi5 2.0的使用说明(可参考:HI5 2.0 交…...
广播及代码实现
广播(Broadcast)是一种网络通信方式,它允许一台设备向网络中的所有其他设备发送消息。广播通常用于在网络上传递一些信息,让所有设备都能接收并处理。在广播中,通信的目标是整个网络而不是特定的单个设备。 向子网中…...
QT应用篇 三、QML自定义显示SpinBox的加减按键图片及显示值效果
QT应用篇 一、QT上位机串口编程 二、QML用Image组件实现Progress Bar 的效果 三、QML自定义显示SpinBox的加减按键图片及显示值效果 文章目录 QT应用篇前言一、qml需求二、使用组件1.SpinBox组件2.SpinBox中QML的使用 总结 前言 记录自己学习QML的一些小技巧方便日后查找 QT的…...
2022年全国职业院校技能大赛网络安全竞赛试题1-10-B模块总结
前言 结尾有对22年国赛题型总结 试题1模块B 网络安全事件响应、数字取证调查和应用安全 B-1任务一:主机发现与信息收集 *任务说明:仅能获取Server1的IP地址 1.通过渗透机Kali2.0对靶机场景进行TCP同步扫描 (使用Nmap工具),并将该操作使用…...
20231228在Firefly的AIO-3399J开发板的Android11的Firefly的AIO-3399J开发板的DTS配置单前置摄像头ov13850
20231228在Firefly的AIO-3399J开发板的Android11的Firefly的AIO-3399J开发板的DTS配置单前置摄像头ov13850 2023/12/28 12:30 开发板:Firefly的AIO-3399J【RK3399】 SDK:rk3399-android-11-r20211216.tar.xz【Android11】 Android11.0.tar.bz2.aa【ToyBr…...
php-fpm运行一段时间,内存不足
目录 一:原因分析 二:解决 三:观察系统情况 php-fpm运行一段时间,内存不足,是什么原因呢。 一:原因分析 1:首先php-fpm的配置 (1)启动的进程数 启动的进程数越多,占用内存越高; 2:其次…...
基于轻量级GhostNet模型开发构建生活场景下生活垃圾图像识别系统
轻量级识别模型在我们前面的博文中已经有过很多实践了,感兴趣的话可以自行移步阅读: 《移动端轻量级模型开发谁更胜一筹,efficientnet、mobilenetv2、mobilenetv3、ghostnet、mnasnet、shufflenetv2驾驶危险行为识别模型对比开发测试》 《基…...
《Linux系列》Linux磁盘MBR分区扩容
文章目录 Linux磁盘MBR分区扩容1.前言2.控制台磁盘扩容3.分区扩容3.1 fdisk3.2 lsblk3.3 扩容分区 4.扩容文件系统4.1 df4.2 扩容文件系统 Linux磁盘MBR分区扩容 1)参考阿里云扩容分区文档,整理MBR分区扩容 2)本文档适用于MBR分区(fdisk -lu查…...
IPv6地址配置
IPv6地址接口配置 IPv6地址结构 一个IPv6地址可以分为两部分: 网络前缀:n比特,相当于IPv4地址中的网络ID 接口标识:128-n比特,相当于IPv4地址中的主机ID 注意: 对于IPv6单播地址来说,如果地址的前三bit不是000,则接口标识必须为64位,如果地址的前三位是000,则没有此…...
Ubuntu20.04 防火墙配置
ubuntu 系统中配置防火墙 ufw(Uncomplicated Firewall)是一个简化的、易于使用的Linux防火墙工具,旨在方便用户管理iptables防火墙规则。 特点 简化的防火墙管理:ufw提供了一个简洁的命令行界面,让您能够轻松地添加、…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
