Go defer延迟函数的用法

Go语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 所在的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。

 当有多个 defer 行为被注册时,它们会以逆序执行(类似栈,即后进先出)

一、defer特性

1. 关键字 defer 用于注册延迟调用。
2. 这些调用直到 return 后才被执。因此,可以用来做资源清理。
3. 多个defer语句,按先进后出的方式执行。
4. defer语句中的变量,在defer声明时就决定了。 
5.延迟调用是在 defer 所在函数结束时进行,函数结束可以是正常返回时,也可以是发生宕机时。

二、defer用途(使用延迟执行语句在函数退出时释放资源)

        处理业务或逻辑中涉及成对的操作是一件比较烦琐的事情,比如打开和关闭文件、接收请求和回复请求、加锁和解锁等。在这些操作中,最容易忽略的就是在每个函数退出处正确地释放和关闭资源。

        defer 语句正好是在函数退出时执行的语句,所以使用 defer 能非常方便地处理资源释放问题。一般使用场景如下:

1) 使用延迟并发解锁

2) 使用延迟释放文件句柄

        文件的操作需要经过打开文件、获取和操作文件资源、关闭资源几个过程,如果在操作完毕后不关闭文件资源,进程将一直无法释放文件资源,defer 后的语句(f.Close())将会在函数返回前被调用,自动释放文件资源。

3)数据库连接释放

4)关闭http请求的response.Body

三、defer使用规则:

3.1、当defer被声明时,其参数就会被实时解析

(1)、下面代码执行结果:

1
0

从可以看到defer输出的值,就是定义时的值。而不是defer真正执行时的变量值 。

也就是说,虽然defer函数会在return之后被调用,但defer里的变量值在defer声明时,就已经定义了。(仅仅针对fmt.Println()函数,或者带参匿名函数

func main() {
	a()
}

func a() {
	i := 0
	defer fmt.Println(i) //输出0,因为i此时就是0
	i++
	defer fmt.Println(i) //输出1,因为i此时就是1
	return
}

(2)、defer后面匿名函数里的输出值,却是在执行时的变量值,是不是懵逼了……

1
1
func a() {
	i := 0
	defer func() { fmt.Println(i) }() // 放在匿名函数里, 输出1,
	i++
	defer fmt.Println(i) //输出1,因为i此时就是1
	return
}

(3)、将(2)当中的匿名函数改成带参数的,结果如下:

             从结果当中可以看出,声明deder时,就将要打印的参数传入匿名带参函数,在执行该defer时,打印的值为defer声明时,函数传传入的值所以fmt.Println()函数相当于一个带参函数,参数为要打印的内容。

1
0
func a() {
	i := 0
	defer func(i int) { fmt.Println(i) }(i) // 将i作为匿名函数参数,然后输出却为0
	i++
	defer fmt.Println(i) //输出1,因为i此时就是1
	return
}

(4)、还可以像下面这样写:

func main() {
	fmt.Println(a())
}

func a() (i int) {
	defer func() {
		i++
		fmt.Println("defer1", i)
	}() // 放在匿名函数里, 输出8,
	i++
	defer fmt.Println("defer2:", i) //输出1,因为i此时就是1
	i = i * 7
	return i
}

输出结果为:

defer2: 1
defer1 8
8

 (5)、又或者这样写:

func main() {
	fmt.Println(a())
}

func a() (i int) {
	defer func() { fmt.Println("defer1", i) }() // 执行defer时,i为7,输出为7,

	defer func(i int) {
		i++
		fmt.Println("defer2", i) // 申明defer1时,i为0,所以i++之后,输出为1,
	}(i)
	i++
	defer fmt.Println("defer3:", i) // 申明defer2时,i为1,所以输出1,
	i = i * 7

	return i
}

输出结果为:

defer3: 1
defer2 1
defer1 7
7

3.2、defer执行顺序为先进后出 

下面代码执行结果为:3210

func b() {
	for i := 0; i < 4; i++ {
		defer fmt.Print(i)
	}
}

 3.3、Go 中 defer 和 return 执行的先后顺序:

        defer、return、返回值三者的执行逻辑应该是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。

        如下所示:

        1、return先返回a,此时a值为0,即,Aa()函数要返回的函数为0;

        2、defer2再执行,a++,a的值由0变为1,所以此处打印为1;

        3、接着defer1再执行,a++,a的值由1变为2,所以此处打印为2;

        4、然后执行main函数打印,打印结果为Aa()return时的值:0

deder2: 1
defer1: 2
returen: 0
func main() {
	fmt.Println("returen:", Aa())
}

func Aa() int {
	var a int
	defer func() {
		a++
		fmt.Println("defer1:", a)

	}()
	defer func() {
		a++
		fmt.Println("deder2:", a)
	}()
	return a
}

3.4、defer执行时,可以实时读取有名返回值

        我们说过defer是在return调用之后才执行的。 这里需要明确的是defer代码块的作用域仍然在函数之内,结合上面的函数也就是说,defer的作用域仍然在c函数之内。因此defer仍然可以读取c函数内的变量。

        下面代码,当执行return 1 之后,i的值就是1。 此时此刻,defer代码块开始执行,对i进行自增操作, i变为2,因此输出defer 2,然后c函数开始返回i,因此,main函数输出结果为c:2。

        所以下面代码执行结果为:

defer 2
c: 2
func main() {
	fmt.Println("c:", c())
}

func c() (i int) {
	defer func() {
		i++
		fmt.Println("defer", i)
	}()
	return 1
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开心码农1号

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值