当前位置: 首页 > news >正文

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提供了一个简洁的命令行界面,让您能够轻松地添加、…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

AI,如何重构理解、匹配与决策?

AI 时代&#xff0c;我们如何理解消费&#xff1f; 作者&#xff5c;王彬 封面&#xff5c;Unplash 人们通过信息理解世界。 曾几何时&#xff0c;PC 与移动互联网重塑了人们的购物路径&#xff1a;信息变得唾手可得&#xff0c;商品决策变得高度依赖内容。 但 AI 时代的来…...