在Golang中,defer关键字用于延迟执行一个函数调用,被延迟的函数会在包裹它的函数即将返回时执行,这个特性让defer成为处理资源释放、执行收尾操作的常用工具,同时也能通过规则控制多个延迟调用的执行顺序。

defer的基础语法
defer的基本用法非常简单,只需要在函数调用前加上defer关键字即可,被延迟的函数会在当前函数结束时执行,无论函数是正常返回还是发生panic。
package main
import "fmt"
func main() {
defer fmt.Println("这是延迟执行的内容")
fmt.Println("这是正常执行的内容")
}
// 输出结果:
// 这是正常执行的内容
// 这是延迟执行的内容
使用defer释放资源
在实际开发中,defer最常见的用途就是释放资源,比如关闭打开的文件、释放申请的锁、关闭数据库连接等,这样可以避免忘记释放资源导致的泄漏问题。
文件资源释放示例
打开文件后使用defer延迟关闭,无论后续文件操作是否成功,文件都会在函数返回前被关闭。
package main
import (
"fmt"
"os"
)
func readFile() {
// 打开文件
file, err := os.Open("test.txt")
if err != nil {
fmt.Println("打开文件失败:", err)
return
}
// 延迟关闭文件,确保函数返回前执行
defer file.Close()
// 后续的文件读取操作
// 即使这里发生错误,file.Close()也会被执行
fmt.Println("文件打开成功,准备读取内容")
}
func main() {
readFile()
}
锁资源释放示例
在加锁后使用defer延迟解锁,避免忘记解锁导致的死锁问题。
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
count int
)
func addCount() {
// 加锁
mu.Lock()
// 延迟解锁,确保函数返回前释放锁
defer mu.Unlock()
count++
fmt.Println("当前count值:", count)
}
func main() {
addCount()
}
defer的执行顺序控制
同一个函数中如果有多个defer语句,它们的执行顺序遵循后进先出的规则,也就是最后被defer的函数会最先执行,类似栈的结构。
package main
import "fmt"
func main() {
defer fmt.Println("第一个defer")
defer fmt.Println("第二个defer")
defer fmt.Println("第三个defer")
fmt.Println("主函数正常执行内容")
}
// 输出结果:
// 主函数正常执行内容
// 第三个defer
// 第二个defer
// 第一个defer
如果需要在循环中执行defer,要注意defer是在函数返回时才执行,循环中的defer不会每次循环结束就执行,通常会包装成函数来使用。
package main
import "fmt"
func process() {
for i := 0; i < 3; i++ {
// 将defer放在匿名函数中,每次循环会立即计算参数
defer func(num int) {
fmt.Println("循环中的defer,当前值:", num)
}(i)
}
}
func main() {
process()
}
// 输出结果:
// 循环中的defer,当前值: 2
// 循环中的defer,当前值: 1
// 循环中的defer,当前值: 0
defer的常见注意事项
- defer函数的参数会在defer语句执行时就被计算确定,而不是在函数返回时才计算。比如
defer fmt.Println(i)中的i值,是defer执行时i的当前值。 - defer可以修改命名返回值,因为命名返回值在函数开始时就已经分配了内存,defer执行时可以对它进行修改。
- 如果defer中发生了panic,后续的defer仍然会执行,直到所有defer执行完毕,程序才会崩溃。
package main
import "fmt"
func test() (result int) {
defer func() {
// 修改命名返回值
result++
}()
return 10
}
func main() {
fmt.Println("返回值:", test()) // 输出:返回值: 11
}
总结
defer是Golang中非常实用的关键字,合理使用可以简化资源释放的逻辑,避免遗漏收尾操作,同时通过后进先出的执行顺序规则,可以灵活控制多个延迟操作的执行次序。开发者在使用时需要注意参数计算的时机、对命名返回值的影响等细节,才能充分发挥defer的作用,写出更可靠的程序。