详解如何在Go中循环中使用Defer关键字示例详解

 更新时间:2023年09月12日 15:54:39   作者:Slagga技术的游戏  
这篇文章主要为大家介绍了详解如何在Go中循环中使用Defer关键字示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

defer在循环中的行为

在Go编程中,defer是一个强大的关键字,它安排在包围它的函数返回时执行一个函数调用。这一特性使我们能够在创建之后立即编写清理活动,如关闭文件或网络连接,增强代码的可读性和可维护性。

然而,在循环中使用defer需要特别注意,因为它可能导致意外的行为甚至内存泄漏。

当在循环中调用defer时,它不会立即执行被推迟的函数。相反,它安排在包围它的函数返回时调用该函数。这意味着,如果你在循环中使用defer,所有被推迟的函数调用都会堆叠起来,只有在循环完成并且包围它的函数返回时才会被执行。

下面是一个简单的例子来说明这一点:

package main
import "fmt"
func main() {
    for i := 0; i < 5; i++ {
        defer fmt.Println(i)
    }
}

在这个例子中,当main函数即将返回时,数字0到4将被打印,而不是在每次迭代后。此外,由于defer的LIFO(后进先出)特性,数字将以相反的顺序打印:4, 3, 2, 1, 0。

可能的内存泄漏

尽管defer在循环中的行为在某些情况下可能是有用的,但它也可能导致问题。例如,如果循环没有终止,被推迟的函数调用会继续堆叠,可能导致内存泄漏。这是因为被推迟的函数调用存储在内存中,直到它们被执行,如果它们没有被执行,那么内存就不会被释放。

引入另一个函数来解决问题

解决这个问题的一个常见方法是在循环的每次迭代中引入另一个函数,并将defer语句放在这个新函数中。这样,被推迟的函数调用将在每次迭代结束时被执行,而不是堆叠起来等待包围函数返回。

下面是我们如何使用这种方法修改前面的例子:

package main
import "fmt"
func main() {
    for i := 0; i < 5; i++ {
        func(n int) {
            defer fmt.Println(n)
        }(i)
    }
}

在这个修改后的例子中,我们引入了一个接受整数参数的匿名函数。我们在循环的每次迭代中使用i作为参数调用这个函数,defer语句在这个匿名函数中。现在,被推迟的fmt.Println(n)调用将在每次迭代结束时被执行,数字0到4将按正确的顺序打印:0, 1, 2, 3, 4。

与文件相关的潜在内存泄漏示例

在这个例子中,我们将创建一些文件,写入它们,但由于defer调用堆叠起来,包围函数永远不会返回,所以我们永远不会关闭它们:

package main
import (
    "os"
    "fmt"
)
func main() {
    for i := 0; i < 1000000; i++ {
        file, err := os.Create(fmt.Sprintf("testfile%d.txt", i))
        if err != nil {
            panic(err)
        }
        defer file.Close()
        _, err = file.WriteString("Test")
        if err != nil {
            panic(err)
        }
    }
}

在这段代码中,我们正在创建一百万个文件,并向每个文件中写入字符串“Test”。defer file.Close() 语句意味着每个文件都将在 main 函数返回时关闭。但是,由于 main 函数在循环完成后才返回,所以直到所有的一百万个文件都被创建并写入后,这些文件才真正被关闭。这可能导致内存泄漏和其他与资源相关的问题,因为程序持有大量的打开文件描述符。

解决文件问题

为了解决这个问题,我们可以在循环中引入另一个函数,并将 defer file.Close() 语句放在这个新函数中。这确保了每个文件在循环的每次迭代结束时都被关闭,而不是等待 main 函数返回:

package main
import (
    "os"
    "fmt"
)
func main() {
    for i := 0; i < 1000000; i++ {
        func(i int) {
            file, err := os.Create(fmt.Sprintf("testfile%d.txt", i))
            if err != nil {
                panic(err)
            }
            defer file.Close()
            _, err = file.WriteString("Test")
            if err != nil {
                panic(err)
            }
        }(i)
    }
}

在这段修改后的代码中,我们引入了一个匿名函数,该函数接受一个整数参数 i。我们在循环的每次迭代中调用此函数,并将 defer file.Close() 语句放在这个匿名函数中。现在,每个文件都在相应的迭代结束时关闭,从而防止了我们在前面的示例中看到的可能的内存泄漏和与资源相关的问题。

结论

在 Go 中,defer 关键字提供了一种强大的方式来安排函数调用在其周围的函数返回时执行。这对于清理任务特别有用,但是在循环中使用 defer 时,重要的是要理解被推迟的调用会堆积起来,直到周围的函数返回。这可能导致意外的行为,甚至内存泄漏。

解决这个问题的一个常见方法是在每次迭代中调用另一个函数,并将 defer 语句放在这个新函数中。这种方法确保推迟的调用在每次迭代结束时执行,而不是堆积起来。理解这些概念及如何有效地使用它们对于编写高效且可读的 Go 代码至关重要。

以上就是详解如何在Go中循环中使用Defer示例详解的详细内容,更多关于Go循环Defer的资料请关注脚本之家其它相关文章!

相关文章

  • Go/C语言LeetCode题解997找到小镇法官

    Go/C语言LeetCode题解997找到小镇法官

    这篇文章主要为大家介绍了Go语言LeetCode题解997找到小镇的法官示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • golang 开启opencv图形化编程

    golang 开启opencv图形化编程

    这篇文章主要为大家介绍了golang 开启opencv图形化编程示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • Go项目在GoLand中导入依赖标红问题的解决方案

    Go项目在GoLand中导入依赖标红问题的解决方案

    这篇文章主要介绍了Go项目在GoLand中导入依赖标红问题的解决方案,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-06-06
  • 如何避免Go语言常见错误之意外的变量隐藏

    如何避免Go语言常见错误之意外的变量隐藏

    在Go语言中,变量隐藏(Variable Shadowing)是一个常见的错误来源,变量隐藏发生在一个内部作用域中声明的变量与外部作用域的变量同名时,这可能导致开发者无意中使用了错误的变量,造成难以追踪的bug,本文讲解一些关于变量隐藏的常见错误和如何避免它们的方法
    2024-01-01
  • golang简易令牌桶算法实现代码

    golang简易令牌桶算法实现代码

    这篇文章主要介绍了golang简易令牌桶算法实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • golang中的jwt使用教程流程分析

    golang中的jwt使用教程流程分析

    这篇文章主要介绍了golang中的jwt使用教程,接下来我们需要讲解一下Claims该结构体存储了token字符串的超时时间等信息以及在解析时的Token校验工作,需要的朋友可以参考下
    2023-05-05
  • Go语言设计模式之结构型模式

    Go语言设计模式之结构型模式

    本文主要聚焦在结构型模式(Structural Pattern)上,其主要思想是将多个对象组装成较大的结构,并同时保持结构的灵活和高效,从程序的结构上解决模块之间的耦合问题
    2021-06-06
  • golang 通过ssh代理连接mysql的操作

    golang 通过ssh代理连接mysql的操作

    这篇文章主要介绍了golang 通过ssh代理连接mysql的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 如何基于Golang实现Kubernetes边车模式

    如何基于Golang实现Kubernetes边车模式

    本文介绍了如何基于Go实现Kubernetes Sidecar模式,并通过实际示例演示创建Golang实现的微服务服务、Docker 容器化以及在 Kubernetes 上的部署和管理,感兴趣的朋友一起看看吧
    2024-08-08
  • GoLang string与strings.Builder使用对比详解

    GoLang string与strings.Builder使用对比详解

    这篇文章主要介绍了GoLang string与strings.Builder使用对比,Builder 用于使用 Write 方法有效地构建字符串。它最大限度地减少了内存复制。零值可以使用了。不要复制非零生成器
    2023-03-03

最新评论