Go语言 defer 详解

一、什么是 defer?

在Go语言中,defer用于注册延迟执行的函数。 这些被推迟的函数会在外层函数返回之前按照后进先出(LIFO)的顺序执行。

一句话总结: 👉 defer就是安排好“临走前要做的事”


二、defer 的基本语法

defer 函数调用

注意:

  • defer后面必须是一个函数调用,不能是函数定义!

  • defer语句会立刻计算参数,但延迟函数体的执行。


三、简单示例:基本用法

package main
​
import "fmt"
​
func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
}

输出结果

hello
world

解释

  • 程序先输出"hello"。

  • main()结束前,执行defer的fmt.Println("world")


四、defer的执行顺序

多个defer时,它们的执行顺序是栈结构(后进先出)。

package main
​
import "fmt"
​
func main() {
    defer fmt.Println("first")
    defer fmt.Println("second")
    defer fmt.Println("third")
}

输出顺序

third
second
first

总结口诀

  • 谁后defer,谁先执行!


五、defer的参数求值时机

很多初学者容易误解:defer语句注册时就计算参数,而不是等到执行时才计算!

看例子:

package main
​
import "fmt"
​
func main() {
    a := 10
    defer fmt.Println("defer a:", a)
    a = 20
    fmt.Println("current a:", a)
}

输出

current a: 20
defer a: 10

解释

  • defer fmt.Println("defer a:", a)在注册时,a的值就是10。

  • 后续a变了,不影响已注册的defer。


六、defer常见应用场景

1. 释放资源

比如关闭文件、释放数据库连接等。

package main
​
import (
    "fmt"
    "os"
)
​
func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer file.Close()  // 确保函数结束前关闭文件
}

2. 解锁操作

mu.Lock()
defer mu.Unlock()

无论函数中间出现什么异常,最后一定会解锁

3. 捕获异常(panic恢复)

package main
​
import "fmt"
​
func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    panic("Something went wrong")
}

七、defer性能影响

  • defer本身是有微小性能开销的(因为要保存调用信息)。

  • 高频、短小函数(如循环千万次)里,频繁使用defer可能影响性能。

  • Go 1.14+ 之后,官方优化了defer,性能开销大大降低了,但极限性能场景下仍要注意。

实际建议

绝大多数业务场景可以放心使用defer,优先保证代码正确性和简洁性。


八、defer搭配匿名函数使用

可以用匿名函数+defer,来灵活控制更多逻辑。

func main() {
    fmt.Println("start")
​
    defer func() {
        fmt.Println("deferred block start")
        fmt.Println("deferred block end")
    }()
​
    fmt.Println("end")
}

输出

start
end
deferred block start
deferred block end

九、defer修改返回值(常考!)

如果函数有具名返回值,defer可以修改它!

func calc() (res int) {
    defer func() {
        res += 100
    }()
    return 1
}

调用结果

fmt.Println(calc())  // 输出 101

流程说明

  • 函数return时,返回值res已经是1。

  • 执行defer,把res加了100。

  • 最终返回101。


十、defer中的陷阱

1. 循环中defer常见坑

如果在循环里defer,会导致所有defer推到循环结束后一起执行

for i := 0; i < 3; i++ {
    defer fmt.Println(i)
}

输出

2
1
0

每次的i值是注册时的值,不是执行时的值!


2. defer闭包变量捕获问题

如果defer里是闭包,要注意变量捕获!

for i := 0; i < 3; i++ {
    defer func() {
        fmt.Println(i)
    }()
}

输出

3
3
3

因为defer里的匿名函数引用了外层变量i,循环完后i已经变成3了!

✅ 正确写法:

for i := 0; i < 3; i++ {
    v := i
    defer func() {
        fmt.Println(v)
    }()
}

这样每次保存独立的v副本,输出:

2
1
0

👉 立即点击链接,开启你的全栈开发之路:Golang全栈开发完整课程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值