defer
为什么需要defer?
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer (延时机制)。
快速入门案例:
func sum(n1 int, n2 int) int {
//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈中(defer栈)
//当函数执行完毕后,再从defer栈中按照先入后出的方式出栈。
defer fmt.Println("ok1 n1=", n1) //defer 3. ok1 n1=10
defer fmt.Println("ok2 n2=", n2) //defer 2. ok2 n2=20
res := n1 + n2 //res = 30
fmt.Println("ok3 res=", res) //1. ok3 res=30
return res
}
func main() {
res := sum(10,20)
fmt.Println("res = ", res) //4. res = 30
}
输出后的结果:
defer 的注意事项和细节:
细节说明:
1)当go执行到一个defer时,不会立即执行defer后的语句,而是将defer 后的语句压入到一个栈中,然后继续执行函数下一个语句。
2)当函数执行完毕后,在从defer 栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制),所以看到上面案例输出的顺序。
3)在defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。
案例:
func sum(n1 int, n2 int) int {
//当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈中(defer栈)
//当函数执行完毕后,再从defer栈中按照先入后出的方式出栈。
defer fmt.Println("ok1 n1=", n1) //defer 3. ok1 n1=10
defer fmt.Println("ok2 n2=", n2) //defer 2. ok2 n2=20
//增加一句话
n1++
n2++
res := n1 + n2
fmt.Println("ok3 res=", res) //1. ok3 res=32
return res
}
func main() {
res := sum(10,20)
fmt.Println("res = ", res) //4. res = 32
}
输出的结果是:
defer 的最佳实践:
defer 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。
说明:
1)在golang编程中的通常做法是,创建资源后,比如(打开了文件句柄,获取了数据库的连接,或者是锁资源),可以执行 defer file.Colse() 和 defer connect.Close()。
2)在 defer 后,可以继续使用创建资源。
3)当函数完毕后,系统会依次从 defer 栈中,取出语句,关闭资源。
4)这种机制非常简洁,程序员不用再为在什么时机关闭资源而烦心了。