golang的defer机制

21 阅读4分钟

 ​编辑

defer的底层机制

为栈操作,栈是一个先进后出的数据结构

func main() {
    fmt.Println("reciprocal")

    for i := 0; i < 10; i++ {
        defer fmt.Println(i)
    }
}

运行结果
reciprocal
9
8
7
6
5
4
3
2
1
0

defer拷贝机制

以下已经发生压栈发生值拷贝数据不再会发生变化

func test() int {
	var i = 1
	defer func(i int) {
		fmt.Println("数据压栈已压栈数据不会在发生变化", i)
	}(i)
	i++
	return i
}

运行结果

数据压栈已压栈数据不会在发生变化 1

函数返回值 2

deferreturn的返回时机(执行顺序)

根据defer内的变量为匿名函数,还是命名函数。函数的返回数据会有差距

  1. 计算并赋值返回值

• 对于匿名返回值的函数,return 语句先会计算并生成返回值,并将其赋值给一个临时变量。

• 对于命名返回值的函数,return 语句会将结果赋值给命名的返回变量,这个变量在函数开始时就已经声明。

  1. 执行 defer 语句

• defer 语句中的函数在 return 语句执行后被调用,但它们的执行顺序是后进先出 (Last In First Out, LIFO) 的。

• defer 函数可以操作命名返回值,从而可能修改最终的返回值。

  1. 返回结果

• 在 defer 函数执行完后,返回最终的结果。

实践匿名函数

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

func Anonymous() int {
	var i int
	defer func() {
		i++
		fmt.Println("defer2 value is ", i)
	}()

	defer func() {
		i++
		fmt.Println("defer1 in value is ", i)
	}()

	return i
}

结果

defer1 in value is  1

defer2 value is  2

0

执行过程

        1. 函数执行到 return i 时,i 的值是 0。

        2. 执行 defer 语句,先执行 defer1,然后执行 defer2,此时 i 变为 2,但 return 的返回值已                固定为 0。

        3. 最终返回值为 0。

实践命名返回值的函数

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

func HasName() (j int) {
	defer func() {
		j++
		fmt.Println("defer2 in value", j)
	}()

	defer func() {
		j++
		fmt.Println("defer1 in value", j)
	}()

	return j
}

结果
defer1 in value 1
defer2 in value 2
2

执行过程

  1. 函数执行到 return j 时,j 的初始值为 0。

  2. 执行 defer 语句,先执行 defer1,j 变为 1,再执行 defer2,j 变为 2。

  3. 最终返回值为 2,因为 defer 修改了命名返回值 j。

总结

匿名返回值函数:return 语句计算出返回值并存储在临时变量中,随后执行 defer 函数,但 defer 对返回值的修改不会影响最终返回值。

命名返回值函数:return 语句返回命名的返回变量,defer 函数能够修改这个变量,因此它会影响最终返回值。

defer 执行顺序的规则

  1. defer 的参数在 defer 语句处计算。

  2. defer 函数在函数返回时按照后进先出的顺序执行。

  3. defer 函数可以读写返回函数的命名返回值。

defer 的使用场景:

1. 关闭资源

在处理文件、数据库连接、网络连接等需要关闭的资源时,defer 确保资源在使用完毕后被正确关闭,即使中间发生了错误。

2. 解锁资源

当使用 sync.Mutex 等锁机制时,defer 确保锁在临界区代码执行完毕后被解锁,从而避免死锁情况的发生。defer 释放分布式锁,防止死锁

3.记录日志或执行收尾工作

defer 可以用来记录函数的退出状态,或者在函数结束前执行一些收尾操作,例如日志记录或计时。

4. 处理错误和恢复(recover)

在可能会引发 panic 的代码中,defer 可以用来捕获和恢复错误,防止程序崩溃。

5. 清理临时状态

在函数内创建了一些临时状态或变量,需要在函数结束时清理时,defer 可以确保这些清理操作被执行。

引用

详解defer实现机制(附上三道面试题,我不信你们都能做对)