Go的Defer简介

什么是延期?

Defer 语句用于在存在 defer 语句的周围函数返回之前执行函数调用。该定义可能看起来很复杂,但通过示例就很容易理解。

例子

package mainimport (  "fmt"
)func finished() {  fmt.Println("Finished finding largest")
}func largest(nums []int) {  defer finished()    fmt.Println("Started finding largest")max := nums[0]for _, v := range nums {if v > max {max = v}}fmt.Println("Largest number in", nums, "is", max)
}func main() {  nums := []int{78, 109, 2, 563, 300}largest(nums)
}

Run in playground

上面是一个简单的程序,用于查找给定切片的最大数量。该largest函数接受一个int切片作为参数并打印该切片的最大数量。函数的第一行包含语句defer finished()。这意味着该finished()函数将在函数返回之前被调用。运行该程序,您可以看到打印出以下输出。

Started finding largest  
Largest number in [78 109 2 563 300] is 563  
Finished finding largest  

函数开始执行并打印上述输出的前两行。在它返回之前,我们的延迟函数finished会执行并打印文本Finished finding largest

延迟方法

Defer 不仅仅限于函数。Defer方法调用也是完全合法的

让我们编写一个小程序来测试一下。

package mainimport (  "fmt"
)type person struct {  firstName stringlastName string
}func (p person) fullName() {  fmt.Printf("%s %s",p.firstName,p.lastName)
}func main() {  p := person {firstName: "John",lastName: "Smith",}defer p.fullName()fmt.Printf("Welcome ")  
}

Run in playground

在上面的程序中,我们第21行调用了Defer。 程序的其余部分是不言自明的。该程序输出,

Welcome John Smith  

论据评价

延迟函数的参数在defer执行语句时计算,而不是在实际函数调用完成时计算。

让我们通过一个例子来理解这一点。

package mainimport (  "fmt"
)func printA(a int) {  fmt.Println("value of a in deferred function", a)
}
func main() {  a := 5defer printA(a)a = 10fmt.Println("value of a before deferred function call", a)}

Run in playground

在上面的程序中,a的值最初为5。 当第 12行执行 defer 语句时。a的值是5,因此这将是被当做printA延迟函数的参数。我们将第 1 3行中的值更改a为 10。该程序输出,

value of a before deferred function call 10  
value of a in deferred function 5  

从上面的输出可以理解,虽然执行 defer 语句后的a值发生了变化10,但实际的延迟函数调用printA(a)仍然打印5

延迟堆栈

当一个函数有多个延迟调用时,它们会被压入堆栈并按后进先出(LIFO)顺序执行。

我们将编写一个小程序,使用延迟堆栈反向打印字符串。

package mainimport (  "fmt"
)func main() {  name := "Naveen"fmt.Printf("Original String: %s\n", string(name))fmt.Printf("Reversed String: ")for _, v := range name {defer fmt.Printf("%c", v)}
}

Run in playground

在上面的程序中,使用for range循环迭代字符串并调用defer fmt.Printf("%c", v)这些延迟调用将被添加到堆栈中。

延迟堆栈

上图表示添加 defer 调用后堆栈的内容。堆栈是后进先出的数据结构。最后压入堆栈的 defer 调用将首先被拉出并执行。在这种情况下defer fmt.Printf("%c", 'n'),将首先执行,因此字符串将以相反的顺序打印。

该程序将输出,

Original String: Naveen  
Reversed String: neevaN  

延迟的实际使用

到目前为止,我们看到的代码示例并未显示 defer 的实际用途。在本节中,我们将研究 defer 的一些实际用途。

Defer 用于无论代码流如何都应该执行函数调用的地方。让我们通过使用WaitGroup的程序示例来理解这一点。我们将首先编写不使用 defer 的程序,然后修改它以使用 defer 并了解 defer 有多么有用。

package mainimport (  "fmt""sync"
)type rect struct {  length intwidth  int
}func (r rect) area(wg *sync.WaitGroup) {  if r.length < 0 {fmt.Printf("rect %v's length should be greater than zero\n", r)wg.Done()return}if r.width < 0 {fmt.Printf("rect %v's width should be greater than zero\n", r)wg.Done()return}area := r.length * r.widthfmt.Printf("rect %v's area %d\n", r, area)wg.Done()
}func main() {  var wg sync.WaitGroupr1 := rect{-67, 89}r2 := rect{5, -67}r3 := rect{8, 9}rects := []rect{r1, r2, r3}for _, v := range rects {wg.Add(1)go v.area(&wg)}wg.Wait()fmt.Println("All go routines finished executing")
}

Run in playground

在上面的程序中,我们创建了一个rect结构体和area的方法。此方法检查矩形的长度和宽度是否小于零。如果是,则打印相应的消息,否则打印矩形的面积。

main函数创建 3 个类型为rect的变量。然后将它们添加到rects 的切片中。 然后使用循环迭代该切片,并在第 37 行中将area方法作为并发调用。37. WaitGroup用于确保 main 函数被阻塞,直到所有 Goroutines 执行完毕。此 WaitGroup 作为参数传递给方法,并且调用wg.Done()方法通知主函数 Goroutine 已完成其工作。如果您仔细观察,您会发现调用*wg.Done()*发生在区域方法返回之前。

无论代码流采用的路径如何,都应在方法返回之前调用 wg.Done(),因此这些调用可以有效地由多个调用替换单个调用

在下面的程序中,我们删除了wg.Done()上面程序中的 3 个调用,并将其替换为defer wg.Done()的单个调用。这使得代码更加简单易懂。

package mainimport (  "fmt""sync"
)type rect struct {  length intwidth  int
}func (r rect) area(wg *sync.WaitGroup) {  defer wg.Done()if r.length < 0 {fmt.Printf("rect %v's length should be greater than zero\n", r)return}if r.width < 0 {fmt.Printf("rect %v's width should be greater than zero\n", r)return}area := r.length * r.widthfmt.Printf("rect %v's area %d\n", r, area)
}func main() {  var wg sync.WaitGroupr1 := rect{-67, 89}r2 := rect{5, -67}r3 := rect{8, 9}rects := []rect{r1, r2, r3}for _, v := range rects {wg.Add(1)go v.area(&wg)}wg.Wait()fmt.Println("All go routines finished executing")
}

Run in playground

该程序输出,

rect {8 9}'s area 72  
rect {-67 89}'s length should be greater than zero  
rect {5 -67}'s width should be greater than zero  
All go routines finished executing  

在上面的程序中使用 defer 还有一个优点。假设我们area使用新if条件向该方法添加另一个返回路径。如果调用wg.Done()没有延迟,我们必须小心并确保我们调用wg.Done()这个新的返回路径。但由于调用wg.Done()被Defer,我们不必担心向该方法添加新的返回路径。

本教程到此结束。祝你有美好的一天。

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

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

相关文章

基于单片机设计的太阳能跟踪器

一、前言 随着对可再生能源的需求不断增长&#xff0c;太阳能作为一种清洁、可持续的能源形式&#xff0c;受到越来越多的关注和应用。太阳能光板通常固定在一个固定的角度上&#xff0c;这限制了它们对太阳光的接收效率。为了充分利用太阳能资源&#xff0c;提高太阳能光板的…

Apache Pulsar 在腾讯云上的最佳实践

导语 由 StreamNative 主办的 Pulsar Meetup Beijing 2023 在2023年10月14日完美落幕&#xff0c;本次活动大咖云集&#xff0c;来自腾讯、滴滴、华为、智联招聘、RisingWave 和 StreamNative 的行业专家们一起&#xff0c;深入探讨 Pulsar 在生产环境中的最佳应用实践&#x…

NoSQL数据库使用场景以及架构介绍

文章目录 一. 什么是NoSQL&#xff1f;二. NoSQL分类三. NoSQL与关系数据库有什么区别四. NoSQL主要优势和缺点五. NoSQL体系框架 其它相关推荐&#xff1a; 系统架构之微服务架构 系统架构设计之微内核架构 鸿蒙操作系统架构 架构设计之大数据架构&#xff08;Lambda架构、Kap…

轧钢厂安全生产方案:AI视频识别安全风险智能监管平台的设计

一、背景与需求 轧钢厂一般都使用打包机对线材进行打包作业&#xff0c;由于生产需要&#xff0c;人员需频繁进入打包机内作业&#xff0c;如&#xff1a;加护垫、整包、打包机检修、调试等作业。在轧钢厂生产过程中&#xff0c;每个班次生产线材超过300件&#xff0c;人员在一…

docker打包container成image,然后将image上传到docker hub

第一步&#xff1a;停止正在运行的容器 docker stop <container_name> eg: docker stop xuanjie_mlir 第二步&#xff1a;将对应的container打包成image docker commit <container_id> <镜像名&#xff1a;版本> eg&#xff1a;docker commit 005672e6d97a…

ChinaSoft 论坛巡礼 | 安全攸关软件的智能化开发方法论坛

2023年CCF中国软件大会&#xff08;CCF ChinaSoft 2023&#xff09;由CCF主办&#xff0c;CCF系统软件专委会、形式化方法专委会、软件工程专委会以及复旦大学联合承办&#xff0c;将于2023年12月1-3日在上海国际会议中心举行。 本次大会主题是“智能化软件创新推动数字经济与社…

京东数据平台:2023年9月京东智能家居行业数据分析

鲸参谋监测的京东平台9月份智能家居市场销售数据已出炉&#xff01; 9月份&#xff0c;智能家居市场销售额有小幅上涨。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;今年9月&#xff0c;京东平台智能家居的销量为37万&#xff0c;销售额将近8300万&#xff0c;同比增…

XUbuntu22.04之解决桌面突然放大,屏幕跟着鼠标移动问题(一百九十)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

【基带开发】AD9361 复乘 com_cmpy_a12_b12

IP核 tb_com module tb_com();reg ad9361_l_clk,rst; initial beginad9361_l_clk0;forever #4.545 ad9361_l_clk~ad9361_l_clk; end initial beginrst1;#9.09 rst0; end wire [63 : 0] m_fll_phase_shift_dout; // fll 输出 dout // FLL Phase Shift com_cmpy_a12_b12 FLL_P…

QT在线安装5.15之前的版本(下载速度飞快)

使用最新的QT在线安装器&#xff0c;安装QT版本时只能安装5.15以及之后的版本&#xff0c;安装QT5.15之前的版本只能通过离线安装的方式&#xff0c;离线安装后还要自己去配置QT&#xff0c;离线安装还有个问题的&#xff0c;后续维护比较麻烦&#xff0c;QT的维护工具还要自己…

记一次并发问题 Synchronized 失效

记一次并发问题 Synchronized 失效 场景&#xff1a;为避免信息提交重复&#xff0c;给事务方法增加了synchronized修饰符&#xff0c;实际场景中仍然无法完全避免重复&#xff0c;原因是因为在第一个线程执行完synchronized代码段后&#xff0c;此时spring还未完成事务提交&a…

JMM讲解

一&#xff1a;为什么要有JMM&#xff0c;它为什么出现&#xff1f; CPU的运行并不是直接操作内存而是先把内存里面的数据读到缓存&#xff0c;而内存的读和写操作的时候会造成不一致的问题。JVM规范中试图定义一种Java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异&…

WPF布局控件之WrapPanel布局

前言&#xff1a;博主文章仅用于学习、研究和交流目的&#xff0c;不足和错误之处在所难免&#xff0c;希望大家能够批评指出&#xff0c;博主核实后马上更改。 概述&#xff1a; 后续排序按照从上至下或从右至左的顺序进行&#xff0c;具体取决于方向属性的值。WrapPanel 位…

海上风电应急救援vr模拟安全培训提高企业风险防范能力

相比传统的发电厂&#xff0c;海上风电作业积累的经验少&#xff0c;风险高&#xff0c;因此为了规范施工人员的行为和操作&#xff0c;保障生产安全进行&#xff0c;开展海上风电VR安全培训具有重要意义。 有助于提高员工的安全意识 通过模拟真实的海上风电作业环境&#xff0…

怎样做好金融投资翻译

我们知道&#xff0c; 金融投资翻译所需的译文往往是会议文献、年终报表、信贷审批等重要企业金融资料&#xff0c;其准确性事关整个企业在今后一段时期内的发展战略与经营成效。尤其像年报&#xff0c;对于上市公司来说更是至关重要的。那么&#xff0c;怎样做好金融投资翻译&…

英国 AI 安全峰会前瞻:为什么是现在,为什么在英国

撰文&#xff1a;Ingrid Lunden 来源&#xff1a;TechCrunch 图片来源&#xff1a;由无界AI生成 人工智能的前景和危害是如今的热门话题。有人说人工智能将拯救我们&#xff0c;可以帮助诊断一些恶性疾病、弥补教育领域的数字鸿沟等。但也有人担心它在战争、安全、错误信息等方…

微信小程序中使用GIF

前言 最近在微信小程序开发时遇到了一个非常复杂的动画&#xff0c;如果要手搓的话需要用canvas一点点弄&#xff0c;比较麻烦&#xff0c;于是打算做一个gif来实现动画效果 根据需求&#xff0c;动画只需播放一次即可&#xff0c;并且设置了一个重播按钮&#xff0c;点击即可重…

【Unity编辑器扩展】艺术字/自定义图片字体生成工具

艺术字在游戏中很常用&#xff0c;由于普通字体样式过于平淡&#xff0c;制作花里胡哨的文字图片作为游戏字体使用&#xff0c;这就是艺术字。 不依赖第三方工具&#xff0c;仅使用Unity自带的Custom Font 一张艺术字图集就能实现这个功能&#xff0c;但是为了便于使用&#…

【机器学习】二、决策树

目录 一、决策树定义&#xff1a; 二、决策树特征选择 2.1 特征选择问题 2.2 信息增益 2.2.1 熵 2.2.2 信息增益 三、决策树的生成 3.1 ID3算法 3.1.1理论推导 3.1.2代码实现 3.2 C4.5 算法 3.2.1理论推导 ​ 3.2.2代码实现 四、决策树的剪枝 4.1 原理 4.2 算法思路&#xff1a…

uniapp循环对象列表---点击列表切换选中不同状态

目录 源码图片最后 源码 <template><view><ul><li v-for"(item, index) in list" click"toggleSelection(index)" :class"{selected: selectedIndex index}">{{ item }}<view :class"{selected: selectedInde…