← 返回首页
defer 的执行顺序是怎样的?defer 的作用或者使用场景是什么?
在 Go 语言中,defer 关键字用于延迟执行函数调用。它的执行顺序、作用以及常见的使用场景如下:
1. defer 的执行顺序
- 后进先出(LIFO):如果一个函数中存在多个
defer语句,它们会按照“先进后出”的顺序被执行。也就是说,最后被注册的defer函数会最先被执行。 - 执行时机:
defer语句在遇到时会立即计算其参数,但真正的函数调用会被推迟到包含该defer的函数即将返回之前(或发生 panic 时)执行。
2. defer 的作用
- 确保资源释放:无论函数是正常返回还是因为发生错误(甚至 panic)而提前退出,
defer都能保证特定的清理代码一定会被执行。 - 提高代码可读性:将资源的分配和释放逻辑放在相邻的位置,使代码逻辑更清晰,避免在多个
return分支中重复编写释放代码。 - 异常处理与恢复:配合
recover()函数,defer可以在程序发生panic时捕获异常,防止程序直接崩溃,从而实现优雅的错误处理。
3. 常见使用场景
- 资源管理(释放资源):最典型的场景是关闭文件(
file.Close())、关闭数据库连接(db.Close())、关闭网络连接等,防止资源泄漏。 - 锁的释放:在并发编程中,获取互斥锁(
mutex.Lock())后,紧接着使用defer mutex.Unlock(),可以确保无论后续逻辑如何执行,锁最终都会被释放,避免死锁。 - 错误处理与 panic 恢复:在函数入口处使用
defer func() { recover() }()来捕获可能发生的 panic,或者在函数返回前统一记录错误日志。 - 耗时统计:在函数开始时记录起始时间,然后使用
defer在函数结束时计算并打印函数的执行耗时。
使用 defer 的注意事项
- 参数求值时机:
defer语句在注册时就会立即计算其函数的参数。如果参数是变量,后续对变量的修改不会影响defer执行时的参数值。 - 循环中慎用:在
for循环中直接使用defer会导致所有的延迟操作都堆积到函数结束时才执行,可能会造成资源(如文件句柄)长时间不释放,甚至耗尽系统资源。 - 性能开销:虽然
defer非常方便,但它会带来微小的性能开销(涉及栈的压入和弹出)。在性能极其敏感的高频调用路径中,应谨慎使用或手动管理资源释放。