一、为什么需要defer
在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等) ,为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer (延时机制)。
二、快速入门案例
func sum(n1 int, n2 int) int {
defer fmt.Println("ok1 n1=", n1) // sum defer栈
defer fmt.Println("ok2 n2=", n2) // sum defer栈
res := n1 + n2 // res = 32
fmt.Println("ok3 res=", res) // (1) ok3 res = 32
return res
}
func main() {
res := sum(10, 20)
}
三、defer使用细节和注意事项
1、当go执行到一个defer时,不会立即执行defer后的语句,而是将defer 后的语句压入到一个栈中[可以称该栈为defer栈,], 然后继续执行函数下一个语句。
2、当函数执行完毕后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈 先入后出的机制)。
3、在defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。
4、如果我们在 返回值的列表时,defer 中使用到返回值的列表的某一个变量,则会影响返回值.
package main
import (
"fmt"
)
func sum(n1 int, n2 int) (res int) {
defer func() {
res = res + 2
fmt.Println("defer res=", res) //32! 37!
}()
res = n1 + n2 // 30
fmt.Println("sum res=", res)//30
//(1) 将这个 res 这值先付给 返回的列表值
//[(1)如果返回的形参列表有名字,则等价于赋值 (2) 如果返回列表没有名字,则相当于将返回值,保存到一个临时变量]
//(2) 再defer
return res + 5 // res=30 , 35 // res = res + 5
}
func main() {
res := sum(10, 20)
fmt.Println("res=", res)
}
四、defer的最佳实践
defer 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。
// defer的最佳实践——关闭文件资源
func DeferDemo6() {
// 关闭文件资源
file =openfile(文件名)
defer file.close()
// 其它代码
}
// defer的最佳实践——关闭数据库资源
func DeferDemo7() {
// 关闭数据库资源
connect =openDataBase()
defer connect.close()
// 其它代码
}
说明:
1)当程序执行到defer ,不会立即执行
2)当该函数执行完毕后,再去执行defer 语句
3)当我们创建要给资源后,可以立即写一个defer ,可以防止我们忘记关闭资源,造成内存泄漏
4)而且在defer