Golang关键字defer

# Golang 关键字 defer 的用法和原理

## 什么是 defer

在 Golang 中,有一个特殊的关键字 defer,它可以让一个函数或者语句在当前函数返回之前执行。defer 的常见用途有:

- 关闭文件、数据库连接、网络连接等资源
- 解锁互斥锁
- 捕获和处理异常
- 打印日志或者调试信息

## 如何使用 defer

要使用 defer,只需要在函数或者语句前加上 defer 关键字,例如:

```go
func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close() // 延迟关闭文件

    data, err := ioutil.ReadAll(file)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(data))
}

在这个例子中,我们使用 defer 来延迟关闭文件,这样无论函数是否正常返回,文件都会被关闭,避免了资源泄露的风险。

defer 的执行顺序

如果一个函数中有多个 defer 语句,它们的执行顺序是什么呢?答案是,它们会按照后进先出(LIFO)的顺序执行,也就是说,最后一个 defer 语句会最先执行,第一个 defer 语句会最后执行。例如:

func main() {
    defer fmt.Println("Hello")
    defer fmt.Println("World")
    defer fmt.Println("Golang")
}

这个程序的输出是:

Golang
World
Hello

defer 的原理

defer 的原理是什么呢?为什么它可以让一个函数或者语句在当前函数返回之前执行呢?其实,defer 的实现是基于一个叫做延迟函数调用栈(deferred function call stack)的数据结构。每当一个函数中有 defer 语句,编译器就会将该 defer 语句对应的函数和参数(如果有的话)压入这个栈中。当函数返回时,编译器会从栈顶开始弹出并执行所有的延迟函数,直到栈为空为止。

这个栈是在运行时动态分配的,所以它的大小是不固定的,可以根据需要增长或缩小。这个栈也是每个 goroutine 独有的,所以不同的 goroutine 之间的 defer 语句是不会相互影响的。

defer 的注意事项

在使用 defer 时,有一些注意事项需要了解:

  • defer 语句的函数和参数(如果有的话)会在 defer 语句所在的作用域结束时就被求值,而不是在延迟函数执行时才被求值。这意味着,如果 defer 语句的函数或参数引用了某个变量,那么它会使用该变量在 defer 语句执行时的值,而不是延迟函数执行时的值。例如:
func main() {
    x := 1
    defer fmt.Println(x) // x 的值是 1
    x = 2
}

这个程序的输出是:

1
  • defer 语句不能在 for 循环或者其他控制流语句中使用,因为这样会导致延迟函数调用栈过大,甚至溢出。如果需要在循环中使用 defer,可以将 defer 语句放在一个匿名函数中,并在每次循环中调用该匿名函数。例如:
func main() {
    for i := 0; i < 10; i++ {
        func() {
            defer fmt.Println(i) // i 的值是循环中的值
        }()
    }
}

这个程序的输出是:

9
8
7
6
5
4
3
2
1
0
  • defer 语句不能在 Go 语句中使用,因为 Go 语句会创建一个新的 goroutine,而 defer 语句只能在当前 goroutine 中执行。如果需要在 Go 语句中使用 defer,可以将 defer 语句放在一个匿名函数中,并在 Go 语句中调用该匿名函数。例如:
func main() {
    go func() {
        defer fmt.Println("Hello") // Hello 会在这个 goroutine 结束时打印
    }()
}

总结

defer 是 Golang 中一个非常有用的关键字,它可以让我们在当前函数返回之前执行一些操作,例如关闭资源,解锁互斥锁,处理异常等。defer 的实现是基于一个延迟函数调用栈的数据结构,它会按照后进先出的顺序执行所有的延迟函数。在使用 defer 时,需要注意一些细节,例如函数和参数的求值时机,循环和控制流语句的影响,Go 语句的限制等。

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值