迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 > 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 语句

发布时间:2024/02/02 浏览次数:184 分类:编程语言

本教程演示了如何在 C# 中使用 goto 以及何时使用它会有所帮助本教程将演示如何在 C# 中使用 goto 语法,并提供一些代码中的实际使用示例。

在 Python 中是否存在 goto 语句

发布时间:2023/12/20 浏览次数:197 分类:Python

本文为你提供了 Python 中是否存在 goto 语句的答案。本文为你提供了 Python 中是否存在 goto 语句的答案。基本上,Python 不支持 goto 语句。

Java Goto

发布时间:2023/08/07 浏览次数:83 分类:Java

与其他编程语言不同,Java 没有 goto。 相反,Java 包含关键字 label。关键字 label 的作用是改变程序的流程,根据指定的条件跳转到程序的另一段。

Python 错误 Valueerror: Expected 2d Array, Got 1d Array Instead

发布时间:2023/05/30 浏览次数:293 分类:Python

当我们在 numpy 中传递一维数组而不是二维数组时,会发生错误 ValueError: Expected 2D array, got 1D array instead 。如您所知,每种编程语言都会遇到很多错误,有些是在运行时,有些是在编译时。 Pyth

扫一扫阅读全部技术教程

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

最新推荐

教程更新

热门标签

扫码一下
查看教程更方便