迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > Go >

Go 语言 深入理解 defer关键字及defer实践

作者:迹忆客 最近更新:2023/04/27 浏览次数:

Go 语言 defer(延迟) 关键字基本用法详解 这篇文章中,我们详细介绍了 defer关键字的作用以及通过简单示例来了解了defer函数的参数问题。

下面我们对defer进行一个比较深入的探讨

defer 的栈

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

我们的说明离不开代码,我们将编写一个小程序,它使用一堆 defers 反向打印一个字符串。

package main

import (  
    "fmt"
)

func main() {  
    name := "jiyik.com"
    fmt.Printf("Original String: %s\n", string(name))
    fmt.Printf("Reversed String: ")
    for _, v := range []rune(name) {
        defer fmt.Printf("%c", v)
    }
}

运行示例

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

 

go defer-stack
go defer-stack

 

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

上面程序的执行结果如下

go-stack-reverse-string


defer的实际使用

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

Defer 用于在不考虑代码流的情况下应该执行函数调用的地方。 让我们通过程序示例来理解这一点。 我们将首先编写不使用 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")
}

运行示例

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

main 函数创建了 3 个 rect 类型的变量 r1r2r3。 然后将它们添加到 rects 切片中。 然后使用 for range 循环迭代这个切片,并且 area 方法作为并发 Goroutine 调用。 WaitGroup wg 用于确保 main 函数被阻塞,直到所有 Goroutines 执行完毕。 这个 WaitGroup 作为参数传递给 area 方法,area 方法调用 wg.Done()。 通知主函数 Goroutine 已完成其工作。 如果仔细观察,就会发现这些调用恰好在 area 方法返回之前发生。 wg.Done() 应该在方法返回之前调用,而不管代码流采用的路径如何,因此这些调用可以有效地替换为单个 defer 调用。

我们如果运行上面的程序,每次运行输出的结果的顺序是不同的

go no defer 示例

让我们使用 defer 重写上面的程序。

在下面的程序中,我们删除了上面程序中的 3 个 wg.Done() 调用,并将其替换为单个 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")
}

运行示例

上面程序的输出结果如下

go defer 示例

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

转载请发邮件至 1244347461@qq.com 进行申请,经作者同意之后,转载请以链接形式注明出处

本文地址:

相关文章

使用 C 语言中的 goto 语句

发布时间:2023/05/07 浏览次数:79 分类:C语言

本文介绍了如何在 C 语言中使用 goto 语句。使用 goto 语句在 C 语言中实现循环 goto 关键字是 C 语言的一部分,它提供了一个做无条件跳转的结构。

Django 中的 Slug

发布时间:2023/05/04 浏览次数:173 分类:Python

本篇文章旨在定义一个 slug 以及我们如何使用 slug 字段在 Python 中使用 Django 获得独特的帖子。

Django ALLOWED_HOSTS 介绍

发布时间:2023/05/04 浏览次数:181 分类:Python

本文展示了如何创建您的 Django 网站,为公开发布做好准备,如何设置 ALLOWED_HOSTS 以及如何在使用 Django 进行 Web 部署期间修复预期的主要问题。

Django 中的 Select_related 方法

发布时间:2023/05/04 浏览次数:129 分类:Python

本文介绍了什么是查询集,如何处理这些查询以及我们如何利用 select_related() 方法来过滤 Django 中相关模型的查询。

在 Django 中上传媒体文件

发布时间:2023/05/04 浏览次数:198 分类:Python

在本文中,我们简要介绍了媒体文件以及如何在 Django 项目中操作媒体文件。

Django 返回 JSON

发布时间:2023/05/04 浏览次数:106 分类:Python

在与我们的讨论中,我们简要介绍了 JSON 格式,并讨论了如何借助 Django 中的 JsonResponse 类将数据返回为 JSON 格式。

在 Django 中创建对象

发布时间:2023/05/04 浏览次数:59 分类:Python

本文的目的是解释什么是模型以及如何使用 create() 方法创建对象,并了解如何在 Django 中使用 save() 方法。

扫一扫阅读全部技术教程

社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便