【第二十二篇】GO语言_函数中的-defer🔥🔥

88 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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的细节说明:

  1. 当 go 执行到一个 defer 时,不会立即执行 defer 后的语句,而是将 defer 后的语句,压入到一个栈中,然后继续执行函数下一个语句。
  2. 当函数执行完毕之后,在从defer栈中,依次从栈顶取出语句执行(注:遵守栈,先入后出的机制,所以同学们看到前面案例输出的顺序。)
  3. 在 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()
	// 其他代码
}

对上面代码的总结:

  1. 在 golang 编制中的通常做法是,创建资源之后,比如说(打开文件,获取了数据的链接或者是锁资源),可以理解file = openfile(文件名) 、connect = openDatabse()
  2. 在 defer 后,可以继续使用创建资源。
  3. 当函数执行完毕后,系统会依次从defer 栈中,取出语句,关闭资源。
  4. 这种机制,非常简洁,程序员不用再为什么时机关闭资源而烦心。