持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
一、defer的基本使用:
一、为什么需要defer:
在函数中,程序员经常需要创建资源(比如:数据库链接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go 的设计者提供defer(延时机制)
二、快速入门案例:
package main
import (
"fmt"
)
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的细节说明:
- 当 go 执行到一个 defer 时,不会立即执行 defer 后的语句,而是将 defer 后的语句,压入到一个栈中,然后继续执行函数下一个语句。
- 当函数执行完毕之后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈,先入后出的机制,所以同学们看到前面案例输出的顺序。)
- 在 defer 将语句放入到栈时,也会将相关的值拷贝同时入栈。请看下面代码
package main
import (
"fmt"
)
func sum(n1 int, n2 int) int {
// 当执行到 defer时,暂时不执行,会将 defer 后面的语句压入到独立的栈中(defer栈)
// 当函数执行完毕后,再从defer栈,按照先入后出的方式出栈,然后执行。
// 这里补充一句,当 defer 压入栈的时候,连带数值也被压入了,所以 n1 = 10 、n2 = 20
defer fmt.Println("OK1 n1=", n1) // defer // 3. OK1 n1 = 10
defer fmt.Println("OK2 n2=", n2) // defer // 2. OK2 n2 = 20
// 增加一句话
n1++ // n1 = 11
n2++ // n2 = 21
res := n1 + n2 // res = 32
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 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。【看下面的代码】
func test(){
// 关闭文件资源
file = openfile(文件名)
defer file.close()
// 其他代码
}
func test() {
// 释放数据库资源
connect = openDatabse()
defer connect.close()
// 其他代码
}
对上面代码的总结:
- 在 golang 编制中的通常做法是,创建资源之后,比如说(打开文件,获取了数据的链接或者是锁资源),可以理解
file = openfile(文件名) 、connect = openDatabse() - 在 defer 后,可以继续使用创建资源。
- 当函数执行完毕后,系统会依次从defer 栈中,取出语句,关闭资源。
- 这种机制,非常简洁,程序员不用再为什么时机关闭资源而烦心。