Go的Defer简介

news2025/1/18 11:04:37

什么是延期?

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

例子

package main

import (  
    "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 main

import (  
    "fmt"
)


type person struct {  
    firstName string
    lastName 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 main

import (  
    "fmt"
)

func printA(a int) {  
    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a)
    a = 10
    fmt.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 main

import (  
    "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 main

import (  
    "fmt"
    "sync"
)

type rect struct {  
    length int
    width  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.width
    fmt.Printf("rect %v's area %d\n", r, area)
    wg.Done()
}

func main() {  
    var wg sync.WaitGroup
    r1 := 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 main

import (  
    "fmt"
    "sync"
)

type rect struct {  
    length int
    width  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.width
    fmt.Printf("rect %v's area %d\n", r, area)
}

func main() {  
    var wg sync.WaitGroup
    r1 := 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.coloradmin.cn/o/1166066.html

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

相关文章

XR Interaction ToolKit

一、简介 XR Interaction Toolkit是unity官方的XR交互工具包。 官方XRI示例地址&#xff1a;https://github.com/Unity-Technologies/XR-Interaction-Toolkit-Examples 2023.3.14官方博客&#xff0c;XRIT v2.3 https://blog.unity.com/engine-platform/whats-new-in-xr-int…

ERR operation not permitted

ERR operation not permitted E:\Document_Redis_Windows\redis-2.4.5-win32-win64\64bit redis.conf auth 123456 redis requirepass_requirepass redis-CSDN博客

PP-Matting:trimap free的高精度自然图像抠图

论文&#xff1a;https://arxiv.org/abs/2204.09433 代码&#xff1a;https://github.com/PaddlePaddle/PaddleSeg 1、动机 在自然图像抠图领域&#xff0c;大多数方法都是基于Trimap来做抠图&#xff0c;这种trimap-based的方式在抠图时还需要用户绘制一个trimap作为模型输入&…

Vue项目运行时报错:‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件

报错原因及解决 1.package.json 文件中未定义依赖项vue/cli-service&#xff0c;因此在 npm install 之后并没有安装vue/cli-service 依赖&#xff1b; 解决&#xff1a;项目目录下执行命令&#xff0c;npm i -D vue/cli-service。2.第1步排查后&#xff0c;还是报同样的错&a…

Django-vue-admin 滚动监听,锚点定位目录

就是实现滑动内容&#xff0c;目录也跟着滚动&#xff0c;同时点击目录&#xff0c;内容会滑动到指定位置 试过很多&#xff0c;反正都不适用Django-vue-admin框架&#xff0c;唯有这个功能可以&#xff0c;只是样式按照自己想要的改改就行&#xff0c; https://blog.csdn.ne…

【深度学习】快速制作图像标签数据集以及训练

快速制作图像标签数据集以及训练 制作DataSet 先从网络收集十张图片 每种十张 定义dataSet和dataloader import glob import torch from torch.utils import data from PIL import Image import numpy as np from torchvision import transforms import matplotlib.pyplot…

这才是当今生成式人工智能的根本性问题!

原创 | 文 BFT机器人 01 引言 近年来&#xff0c;生成式人工智能产品层出不穷&#xff0c;ChatGPT火爆出圈后&#xff0c;百度、谷歌等科技大佬争相研究生成式人工智能产品&#xff0c;将该技术的普及程度提升到了一个新的水平。然而&#xff0c;生成式人工智能的运营需要高昂…

谷歌浏览器解决跨域问题配置记录

在访问时出现has been blocked by CORS policy: Responspreflight request doesn’t pass access control checlAccess-Control-A1low-Origin" header is present onrequested resource. 出现跨域问题 1.先关闭浏览器 2.创建一个目录&#xff0c;文件夹记住路径 3.点击谷…

高德地图撒点组件

一、引入amap地图库 - public/index.html <script type"text/javascript">window._AMapSecurityConfig {securityJsCode: 地图密钥 }</script><scripttype"text/javascript"src"https://webapi.amap.com/maps?v1.4.8&key111111…

rpm 软件包管理工具

RPM&#xff08;RedHat Package Manager&#xff09;&#xff0c;RedHat软件包管理工具。 rpm 查询 rpm -qa #查询所有包(query all)rpm -qa |grep firefox #firefox-102.15.0-1.el7.centos.x86_64rpm -qi | grep firefox #(query information) #Name : firefox #…

Flink日志采集-ELK可视化实现

一、各组件版本 组件版本Flink1.16.1kafka2.0.0Logstash6.5.4Elasticseach6.3.1Kibana6.3.1 针对按照⽇志⽂件⼤⼩滚动⽣成⽂件的⽅式&#xff0c;可能因为某个错误的问题&#xff0c;需要看好多个⽇志⽂件&#xff0c;还有Flink on Yarn模式提交Flink任务&#xff0c;在任务执…

嵌入式学习的两大误区

误区一、全身投入学习桌面或服务器版本Linux系统很多想学嵌入式Linux 的同学经常问我&#xff0c;我不会Linux系统&#xff0c;怎么学习嵌入式Linux开发&#xff0c;于是他们就花费了大量的精力和时间去研究学习桌面版本Linux系统的使用&#xff0c;什么redhat 、federo&#x…

IDEA启动报端口占用

方法一 netstat -ano | findstr :1099 这将列出正在使用1099端口的进程的相关信息&#xff0c;包括进程ID&#xff08;PID&#xff09;。查找使用1099端口的进程ID&#xff0c;并记下该进程的ID号。输入以下命令并按Enter键执行&#xff0c;其中PID是你在上一步中找到的进程ID…

Openssl生成证书-nginx使用ssl

Openssl生成证书并用nginx使用 安装openssl yum install openssl -y创库目录存放证书 mkdir /etc/nginx/cert cd /etc/nginx/cert配置本地解析 cat >>/etc/hosts << EOF 10.10.10.21 kubernetes-master.com EOF10.10.10.21 主机ip、 kubernetes-master.com 本…

【Unity实战】最全面的库存系统(三)

文章目录 先来看看最终效果前言新增脚本获取唯一ID保存和加载保存地面物品将玩家快捷栏和背包合并快捷栏物品显示完结先来看看最终效果 前言 本期紧跟着上期,继续来完善我们的库存系统,实现物品背包仓库数据的存储和加载功能 新增脚本获取唯一ID 新增脚本,自定义控制只读…

超详细Linux搭建Hadoop集群

一、给计算机集群起别名——互通 总纲&#xff1a; 1、准备3台客户机&#xff08;关闭防火墙、静态IP、主机名称都设置好&#xff09; 2、安装JDK&#xff08;可点击&#xff09; 3、配置环境变量 4、安装Hadoop 5、配置hadoop的环境变量 6、配置集群 7、群起测试 1.1、环境准备…

素材搜罗利器!产品设计必须知道的13款最佳网站!

灵感素材类 1.即时设计 在网页中搜索“即时设计”&#xff0c;进入官网后登录账号&#xff0c;之后进入「资源广场」版块便能看到即时设计提供的上万条设计素材。在搜索框内根据需要进行搜索&#xff0c;比如输入“网页设计”&#xff0c;便会看到即时设计提供的网页设计素材…

代码训练营第59天:动态规划part17|leetcode647回文子串|leetcode516最长回文子序列

leetcode647&#xff1a;回文子串 文章讲解&#xff1a;leetcode647 leetcode516&#xff1a;最长回文子序列 文章讲解&#xff1a;leetcode516 DP总结&#xff1a;动态规划总结 目录 1&#xff0c;leeetcode647 回文子串。 2&#xff0c;leetcode516 最长回文子串&#xff1…

实验室装修公司的线上推广成功案例_上海添力网络科技

2018年7月&#xff0c;也是我的书《快速见效的企业网络营销方法 B2B 大宗B2C》出版后两个月&#xff0c;某装修公司的市场部总监在阅读完这本书后&#xff0c;找到了我&#xff0c;希望能帮到他们公司提升线上获客能力。 当时他们已经成立了线上推广团队&#xff0c;配置了SEM岗…

echarts中 对seriesLayoutBy的理解

https://echarts.apache.org/handbook/zh/concepts/dataset/ ‘row’: 系列被安放到 dataset 的行上面。 这里x轴是目录轴&#xff0c;那么一列就是一个系列 ‘column’: 默认值。系列被安放到 dataset 的列上面。 用自己的话总结就是&#xff1a; 当 seriesLayoutBy 为行时&…