Go Defer 使用场景

339 阅读2分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

defer特性:

  1. 关键字 defer 用于注册延迟调用。
  2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。
  3. 多个defer语句,按先进后出的方式执行。
  4. defer语句中的变量,在defer声明时就决定了。

defer用途:

  1. 关闭文件句柄
  2. 锁资源释放
  3. 数据库连接释放
package main

import "fmt"

func main() {
    var users [5]struct{}
    for i := range users {
        defer fmt.Println(i)
    }
}

输出:4 3 2 1 0 ,defer 是先进后出

package defer_test

import (
	"fmt"
	"testing"
)

func TestDefer1(t * testing.T) {
	var users [5]struct{}
	for i := range users {
		defer func() { fmt.Println(i) }()
	}
}

输出结果: 4 4 4 4 4 是否在想一个问题,输出结果不是 4 3 2 1 0 , 由于是闭包用到的变量 i 在执行的时候已经变成了4 , 所以输出全部是 4, 如何正常输出。4 3 2 1 0 ? 不用闭包,换成函数即可。

func TestDefer2(t *testing.T) {
	var users [5]struct{}
	for i := range users {
		defer Print(i)
	}
}

func Print(i int) {
	fmt.Println(i)
}

函数的输出结果是 : 4 3 2 1 0

再看个结构体的例子

func (t *Users) GetName(){
	fmt.Println(t.name)
}

func TestDefer3(t * testing.T){
	 list := []Users{{"乔峰"}, {"慕容复"},{"欧阳克"}}
	 for _, t := range list {
	 	defer t.GetName()
	 }
}

发现没,输出的结果竟然是 欧阳克 欧阳克 欧阳克

不是欧阳克 慕容复 乔峰

如何要输出 : 欧阳克 慕容复 乔峰

写个函数:

type Users struct {
	name string
}


func (t *Users) GetName(){
	fmt.Println(t.name)
}

func GetName(t Users){
	t.GetName()
}

func TestDefer4(t *testing.T) {
	list := []Users{{"乔峰"}, {"慕容复"},{"欧阳克"}}
	for _, t := range list {
		defer GetName(t)
	}
}

函数输出结果: 欧阳克 慕容复 乔峰

如果不想多写一个函数,输出 欧阳克 慕容复 乔峰

要怎么搞?复制一份即可

type Users struct {
	name string
}

func (t *Users) GetName() {
	fmt.Println(t.name)
}

func TestDefer5(t *testing.T) {
	list := []Users{{"乔峰"}, {"慕容复"}, {"欧阳克"}}
	for _, t := range list {
		t1 := t
		defer t1.GetName()
	}
}

输出结果: 欧阳克 慕容复 乔峰

defer 后面的语句在执行的时候,函数调用的参数会保存起来,复制一份,但是不执行。

多个 defer 注册,采用的是 FILO 先进后出的方式,哪怕函数发生错误, 这些调用依然会被执行。

func users(i int) {
	defer println("北丐")
	defer println("南帝")

	defer func() {
		println("西毒")
		println(10 / i) // 异常未被捕获,逐步往外传递,最终终止进程。
	}()

	defer println("东邪")
}
func TestDefer6(t *testing.T) {
	users(0)
	println("武林高手排行榜,这里不会被输出")
}

defer 与 return

在有命名返回值的函数中, 执行 return "风清扬"的时候实际上已经将 s 的值重新赋值为 风清扬

func Users2() (s string) {
   s = "乔峰"
   defer func() {
   	fmt.Println("延迟执行后:" + s)
   }()

   return "清风扬"
}

func TestDefer7(t *testing.T) {
   Users2() // 输出:延迟执行后:清风扬
}

输出结果:

延迟执行后:清风扬
--- PASS: TestDefer7 (0.00s)
PASS