Golang defer关键字

defer特性

1.关键字defer 用于注册延迟调用

2.这些调用直到return 前才被执行。因此,可以用来做资源清理

3.多个defer语句,按先进后出的方式执行

4.defer语句中的变量,在defer生命时就决定了

defer用途

1.关闭文件句柄

2.锁资源释放

3.数据库连接释放

defer用例

1.多个defer输出顺序
Go 复制代码
func main() {
	for i := 0; i < 3; i++ {
		defer fmt.Println(i) // 1. 这三个 defer 语句会按照 i=0, i=1, i=2 的顺序被推入栈中。
	}
	fmt.Println("循环结束") // 2. 循环结束,打印 "循环结束"。

	if true {
		defer fmt.Println("条件语句中的 defer") // 3. 这个 defer 语句被推入栈中。
	}

	defer fmt.Println("最后的 defer") // 4. 这个 defer 语句被推入栈中。

	fmt.Println("main 函数执行中") // 5. 打印 "main 函数执行中"。
}

//结果
//循环结束
//main 函数执行中
//最后的 defer
//条件语句中的 defer
//2
//1
//0
2.defer遇到循环
Go 复制代码
//第一个例子
package main

import "fmt"

type Test struct {
    name string
}

func (t *Test) Close() {
    fmt.Println(t.name, " closed")
}
func main() {
    ts := []Test{{"a"}, {"b"}, {"c"}}
    for _, t := range ts {
        defer t.Close()
    }
}

//结果:c closed 
//c closed
//c closed
Go 复制代码
//第二个例子
package main

import "fmt"

type Test struct {
    name string
}

func (t *Test) Close() {
    fmt.Println(t.name, " closed")
}
func Close(t Test) {
    t.Close()
}
func main() {
    ts := []Test{{"a"}, {"b"}, {"c"}}
    for _, t := range ts {
        defer Close(t)
    }
}
//结果
//c  closed
//b  closed
//a  closed

第一个例子中,当defer语句被执行时,它都会捕获t当前值的副本,并不是t的地址。由于t是在循环遍历的,每次迭代都会创建t的副本,而不是t的地址。由于t是在循环中逐一遍历的,每次迭代都会创建t的新副本。因此当main函数结束时,defer语句会按照后进先出的顺序执行,但是每个defer调用都会打印循环结束时t的最终副本,即"c"。

第二个例子中的close函数接受一个Test类型的参数,并且调用这个参数的Close方法。由于Close是一个单独的函数,它接收t的副本作为参考。当defer调用Close(t)时,它传递了t的当前值的副本给Close函数。这意味着每个defer调用都捕获了循环中t的当前值的副本。

3.defer +recover 捕获异常
Go 复制代码
package main

import "fmt"

func safeDivide(a, b int) (int, error) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    if b == 0 {
        return 0, fmt.Errorf("cannot divide by zero")
    }
    
    return a / b, nil
}

func main() {
    result, err := safeDivide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
    }
    fmt.Println("Result:", result)
}
4.defer 遇到闭包
Go 复制代码
package main

import "fmt"

func main() {
    var whatever [5]struct{}
    for i := range whatever {
        defer func() { fmt.Println(i) }()
    }
}
//结果
//4
//4
//4
//4
//4

这是因为闭包用到的变量i在执行时就已经变成4了,所以输出全是4

注意事项

注意事项

  1. 性能 :过度使用 defer 可能会影响性能,因为每个 defer 调用都会创建额外的栈帧。

  2. 控制流defer 语句的执行顺序可能与直觉相反,需要仔细设计以确保逻辑正确。

  3. 错误处理defer 语句中的错误处理代码应该能够处理所有可能的错误情况。

  4. 嵌套使用 :嵌套使用 defer 时,确保外层的 defer 能够正确处理内层可能抛出的错误。

相关推荐
yaoxin52112311 分钟前
470. Java 反射 - Member 接口与 AccessFlag
java·开发语言·python
groundhappy19 分钟前
idalib安装和codex ida-mcp配置
linux·开发语言·python
FF2501_940228581 小时前
Grid 构建月历网格:7 列模板 + 日期占位算法
后端·华为·harmonyos·鸿蒙系统
神奇小汤圆1 小时前
线程池的 DiscardPolicy 为什么是「静默杀手」?一个生产事故的完整复盘
后端
大鸡腿同学1 小时前
个人博主 AI 剪辑实操:口播视频 3 步出片,省 80% 剪辑时间
后端
小钻风33661 小时前
Spring Boot 文件上传详解:深入理解 MultipartFile 的使用与原理
java·开发语言
其美杰布-富贵-李1 小时前
Spring Boot 工程开发全流程说明
java·spring boot·后端
xianjixiance_2 小时前
HarmonyOS应用开发实战:萌宠日记 - 整体架构设计与技术选型解析
后端
掘金者阿豪2 小时前
数据库优化器到底在想什么:一个SQL今天能跑明天崩的背后
后端
b130538100492 小时前
HarmonyOS应用开发实战:萌宠日记 - 回调函数模式
后端