• go defer简介


    思考

    开始之前,先考虑下下面的代码的执行结果:

    package main
    import "fmt"
    func test() int {
    i := 0
    defer func() {
    fmt.Println("defer1")
    }()
    defer func() {
    i += 1
    fmt.Println("defer2")
    }()
    return i
    }
    func main() {
    fmt.Println("return", test())
    }

    defer介绍

    defer 是 Go 编程语言中的一个关键字,用于在函数执行结束后延迟执行指定的函数调用。defer 的使用非常灵活,它通常用于执行一些清理操作、资源释放、日志记录等任务。以下是对 defer 的详细介绍:

    1. defer 的语法

      • defer 后面跟随一个函数调用,该函数会在包含 defer 语句的函数执行完毕后被调用。
      • 语法示例:defer someFunction()
    2. 执行时机

      • defer 函数调用会在包含 defer 语句的函数返回之前执行,即使在函数中间有 return 语句也是如此。
      • 这确保了 defer 中的操作在函数结束时始终执行,无论函数是正常返回还是出现异常。
    3. 多个 defer 语句

      • 一个函数可以包含多个 defer 语句,它们会以后进先出(LIFO)的顺序执行。
      • 这意味着最后一个出现的 defer 语句会最先执行,而最先出现的 defer 语句会最后执行。
    4. 常见用途

      • 资源释放defer 常用于关闭文件、释放锁、释放内存等资源管理任务,确保资源在函数结束时得到正确释放。
      • 错误处理defer 可以用于记录错误日志或执行清理操作,以确保即使发生错误,资源也能得到释放。
      • 跟踪代码执行defer 还可以用于记录函数的执行情况,以进行性能分析或跟踪代码路径。
    5. 示例
      下面是一个使用 defer 的示例,演示了文件的打开和关闭操作:

      func readFile(filename string) error {
      file, err := os.Open(filename)
      if err != nil {
      return err
      }
      defer file.Close() // 确保文件在函数返回前关闭
      // 文件操作...
      return nil
      }
    6. 注意事项

      • defer 不仅用于函数的返回,还可以用于方法(类似于面向对象编程中的析构函数)。
      • defer 中的参数会在 defer 语句执行时被求值,因此如果你有多个 defer 语句使用相同的参数,它们会被依次求值。
      • 在某些情况下,要特别小心 defer 中的闭包,以避免出现意外的行为。

    defer执行时机

    defer 语句中的函数调用会在包含 defer 语句的函数返回之前执行。无论函数是正常返回还是在执行中发生了 panic,defer 中的函数都会按照后进先出(LIFO)的顺序执行。这确保了在函数结束时进行清理和释放资源,以及在函数执行期间处理错误或日志记录等任务。

    以下是关于 defer 执行时机的详细解释:

    1. 正常返回时的 defer 执行

      • 在函数执行过程中,当遇到 defer 语句时,不会立即执行 defer 中的函数调用,而是将它们压入一个栈中,以便在函数返回时执行。
      • 当函数执行完毕并准备返回时,栈中的 defer 函数调用会按照后进先出的顺序执行,确保最后一个 defer 最先执行。
    2. 发生 panic 时的 defer 执行

      • 如果函数在执行中发生 panic(异常),同样会执行 defer 中的函数,然后再传播 panic,这允许在 panic 后执行清理操作。
      • 这可以用来释放资源、记录错误信息、关闭连接等。

    下面是一个示例,说明了 defer 的执行时机:

    func exampleFunction() {
    defer fmt.Println("Deferred 1")
    defer fmt.Println("Deferred 2")
    fmt.Println("Function body")
    panic("Something went wrong")
    }
    func main() {
    exampleFunction()
    }

    在这个示例中,exampleFunction 包含两个 defer 语句和一个 panic。当 exampleFunction 调用时,它首先打印 "Function body",然后执行 defer 中的函数。在 panic 发生后,defer 语句中的函数会按照后进先出的顺序执行。所以,main 函数的输出将是:

    Function body
    Deferred 2
    Deferred 1
    panic: Something went wrong

    正如示例所示,defer 中的函数在函数返回之前或在 panic 发生后都会执行,这使得它在资源管理和错误处理方面非常有用。

    结束

    现在回到最开始的问题,在上面的代码中,test 函数包含两个 defer 语句,以及一个 return 语句。在 main 函数中,我们调用 test 并输出其返回值。让我们来解释每一步并分析输出的结果:

    1. i 初始化为 0
    2. 第一个 defer 语句中的匿名函数只是打印 "defer1",不对 i 进行任何修改。
    3. 第二个 defer 语句中的匿名函数增加了 i 的值,然后打印 "defer2"。

    现在,让我们分析 test 函数的执行流程:

    1. i 初始化为 0
    2. 第一个 defer 语句注册的函数(打印 "defer1")会在函数返回之前执行,但它没有影响 i 的值。
    3. 接下来,第二个 defer 语句注册的函数(增加 i 的值并打印 "defer2")也会在函数返回之前执行,但在执行时,i 的值仍然为 0
    4. return i 语句返回 0

    因此,test 函数返回 0,但在执行过程中,两个 defer 函数都被执行,按照注册的顺序分别打印 "defer1" 和 "defer2"。

    main 函数中,我们调用 test 并输出其返回值,因此最终的输出是:

    defer2
    defer1
    return 0

    这是因为 defer2defer1 的输出分别在 test 函数调用结束之前执行,而 return 0 的结果在函数返回后被 main 函数输出。


    孟斯特

    声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
    Author: mengbin
    blog: mengbin
    Github: mengbin92
    cnblogs: 恋水无意


  • 相关阅读:
    2021年9月电子学会图形化一级编程题解析含答案:小狗进圈
    数据结构:线段树(SegNode)
    spring 框架理论
    计算机网络—交换机综合实验
    UE5之5.4 第一人称示例代码阅读2 子弹发射逻辑
    HarmonyOS开发:动态共享包的依赖问题
    -bash: zip: 未找到命令
    Haskell 函数(包括条件表达式,模式匹配)
    零编程数据可视化展示:十个简易案例!
    nextjs13如何进行服务端渲染?
  • 原文地址:https://www.cnblogs.com/lianshuiwuyi/p/17761704.html