编程 Golang 中应该知道的 defer 知识

2024-11-18 13:18:56 +0800 CST views 461

Golang 中你应该知道的 defer 知识(基础应用篇)

1.1 使用场景

资源清理

例如关闭文件、数据库连接或网络连接等。

func readFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    // do something
}

解锁资源

在并发编程中确保锁被解锁、执行 wg.Done 等。

var mu sync.Mutex

func criticalSection() {
    mu.Lock()
    defer mu.Unlock()
    // do something
}

func worker(wg *sync.WaitGroup, id int) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
}

确保函数执行结束后进行后续操作

如记录日志或调试信息、性能分析等。

package main

import (
    "fmt"
    "time"
)

// 记录函数执行时间
func timeTrack(start time.Time, name string) {
    elapsed := time.Since(start)
    fmt.Printf("%s took %s\n", name, elapsed)
}

// 记录并跟踪函数的执行
func someFunction() {
    defer timeTrack(time.Now(), "someFunction")
    defer fmt.Println("Function completed")
    
    fmt.Println("Executing someFunction...")
    time.Sleep(2 * time.Second)
    
    // 模拟多个返回点
    if true {
        return // 即使有 return,defer 仍然会执行
    }
    
    fmt.Println("This line won't be reached")
}

func main() {
    someFunction()
}

通过 defer,我们可以极大简化代码的编写和维护,减少资源泄露和逻辑错误的风险。接下来,我们深入了解 defer 的一些核心概念。

1.2 触发时机

defer 语句会在包围它的函数返回之前执行。无论函数怎样退出(正常返回或是 panic),只要在 returnpanic 前声明了 defer,其语句的执行都将得到保证。

func deferFn1() {
    defer fmt.Println("defer")
    fmt.Println("func")
}
// output:
// func
// defer

func deferFn2() {
    defer fmt.Println("defer")
    return
}
// output:
// defer

func deferFn3() {
    defer fmt.Println("defer")
    panic("panic")
}
// output:
// defer
// panic stack

1.3 执行顺序与使用须知

Go 语言中的 defer 语句遵循栈式调用(LIFO - Last In, First Out),即最后一个声明的 defer 最先执行。

func main() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    fmt.Println("end...")
}
// output:
// end...
// 3
// 2
// 1

此外,defer 的参数会在声明时立即求值,而不是在延迟函数实际执行时求值。这意味着当 defer 语句执行时,已经保存的参数值将被使用。

func main() {
    startedAt := time.Now()
    defer func() {
        fmt.Println(time.Since(startedAt))
    }()
    time.Sleep(time.Second)
}
// output: 1s

1.4 返回值处理

在函数有显式 return 语句时,defer 会在返回值被设置之后执行。特别地,如果函数有命名返回参数,defer 可以修改这些返回参数。

func deferReturnFn() int {
    a := 1
    defer func() {
        a = 2
    }()
    return a
}
// output: 1

在命名返回参数的情况下:

func deferReturnFn() (res int) {
    res = 1
    defer func() {
        res = 2
    }()
    return
}
// output: 2

匿名函数的返回值会被丢弃:

func deferReturnFn() int {
    res := 1
    defer func() int {
        res++
        return res
    }()
    return res
}
// output: 1

1.5 使用注意点

  • 栈式调用defer 语句的执行顺序是后进先出。
  • 匿名函数defer 可以使用闭包捕获外部变量。
  • 返回值处理defer 可以修改命名返回参数。

此外,要注意 defer 的位置:

// good
func readFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    // do something
}

// bad
func readFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        log.Fatal(err)
    }
    // do something
    defer file.Close()
}

1.6 哲学思想

defer 设计背后的哲学思想可以从资源清理的角度理解。传统的资源清理方法通常会使用 goto 或者类似的手段处理多个资源的释放,而 defer 的引入简化了这一过程,让代码更加简洁可读。通过 defer,Go 语言继承了 RAII 的设计思想,减少了程序员的心智负担。

1.7 总结

本文介绍了 defer 的基础应用,从使用场景、执行顺序到返回值处理等多个方面进行了详细探讨。defer 是一个非常强大的关键字,掌握好它可以让我们写出更加优雅和可靠的 Go 代码。

进一步思考:

  • defer 的性能如何?Go 团队做了哪些优化?
  • defer 的设计哲学从源码层面是如何实现的?

这些问题值得深入研究,希望在未来能有机会探讨其中的奥秘。

参考链接

  1. Go 语言规范 - Defer Statements
  2. 11 | 程序中的错误处理:错误返回码和异常捕捉 - 左耳听风 - 极客时间
复制全文 生成海报 编程 Go语言 软件开发 技术知识

推荐文章

Vue3 组件间通信的多种方式
2024-11-19 02:57:47 +0800 CST
PHP解决XSS攻击
2024-11-19 02:17:37 +0800 CST
联系我们
2024-11-19 02:17:12 +0800 CST
html一份退出酒场的告知书
2024-11-18 18:14:45 +0800 CST
Vue3中哪些API被废弃了?
2024-11-17 04:17:22 +0800 CST
php strpos查找字符串性能对比
2024-11-19 08:15:16 +0800 CST
Vue3中如何使用计算属性?
2024-11-18 10:18:12 +0800 CST
前端开发中常用的设计模式
2024-11-19 07:38:07 +0800 CST
在 Rust 生产项目中存储数据
2024-11-19 02:35:11 +0800 CST
Vue 3 中的 Watch 实现及最佳实践
2024-11-18 22:18:40 +0800 CST
推荐几个前端常用的工具网站
2024-11-19 07:58:08 +0800 CST
底部导航栏
2024-11-19 01:12:32 +0800 CST
Vue3中如何处理权限控制?
2024-11-18 05:36:30 +0800 CST
Python上下文管理器:with语句
2024-11-19 06:25:31 +0800 CST
2025年,小程序开发到底多少钱?
2025-01-20 10:59:05 +0800 CST
Go 语言实现 API 限流的最佳实践
2024-11-19 01:51:21 +0800 CST
php 统一接受回调的方案
2024-11-19 03:21:07 +0800 CST
linux设置开机自启动
2024-11-17 05:09:12 +0800 CST
利用Python构建语音助手
2024-11-19 04:24:50 +0800 CST
用 Rust 构建一个 WebSocket 服务器
2024-11-19 10:08:22 +0800 CST
JavaScript 实现访问本地文件夹
2024-11-18 23:12:47 +0800 CST
程序员茄子在线接单