持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
一、defer简介
1.1 为什么需要defer
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer (延时机制)。
1.2 快速入门案例
package main
import "fmt"
func sum(num1 int, num2 int) int {
// 当执行到defer时,会将defer后面的语句压入到独立的栈中(defer栈),暂时不执行。当函数执行完毕后,再从defer栈中按照 先入后出 的方式执行
defer fmt.Println(" num1 = ", num1)
defer fmt.Println(" num2 = ", num2)
result := num1 + num2
fmt.Println("result = ", result)
return result
}
func main() {
// result = 30
// num2 = 20
// num1 = 10
sum(10, 20)
}
二、defer细节说明
2.1 细节说明
- 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中(defer栈),然后继续执行函数下一一个语句。
- 当函数执行完毕后,再从defer栈中,依次从
栈顶取出语句执行(注:遵守栈先入后出的机制) - 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈,请看
案例1
2.2 案例演示
2.2.1 案例1
package main
import "fmt"
func sum(num1 int, num2 int) int {
defer fmt.Println(" num1 = ", num1) // 10
defer fmt.Println(" num2 = ", num2) // 20
// 增加两句话
num1++ // 11
num2++ // 21
result := num1 + num2
fmt.Println("result = ", result)
return result
}
func main() {
// result = 32
// num2 = 20
// num1 = 10
// result_main = 32
result_main := sum(10, 20)
fmt.Println("result_main = ", result_main)
}
2.3 最佳实践
defer最主要的价值是 当函数执行完毕后,可以及时的释放函数创建的资源
2.3.1 案例2(模拟代码)
对文件进行操作,操作完成后关闭
func openFile(){
file = openfile(文件名)
defer file.close()
}
对数据库进行模拟操作
func connectDb(){
connect = openDatabase()
defer connect.close()
}
- 在golang中通常做法是,创建资源后,例(打开文件,获取数据库链接,获取锁资源)。可以执行 defer file.close() / defer connect.close()
- 在defer后,可以继续使用创建资源
- 当函数完毕后,系统会依次从defer栈中,取出语句,关闭资源
- 这种机制很简洁,程序员无需考虑关闭资源的时机