【一Go到底】第二十一天---defer

63 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

一、defer简介

1.1 为什么需要defer

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer (延时机制)。

1.2 快速入门案例

package main

import "fmt"

func sum(num1 int, num2 int) int {
        // 当执行到defer时,会将defer后面的语句压入到独立的栈中(defer栈),暂时不执行。当函数执行完毕后,再从defer栈中按照 先入后出 的方式执行
	defer fmt.Println(" num1 = ", num1)
	defer fmt.Println(" num2 = ", num2)
	result := num1 + num2
	fmt.Println("result = ", result)
	return result
}

func main() {
	// result =  30
	// num2 =  20
	// num1 =  10
	sum(10, 20)
}


二、defer细节说明

2.1 细节说明

  1. 当go执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入到一个栈中(defer栈),然后继续执行函数下一一个语句。
  2. 当函数执行完毕后,再从defer栈中,依次从栈顶取出语句执行(注:遵守栈先入后出的机制)
  3. 在defer将语句放入到栈时,也会将相关的值拷贝同时入栈,请看 案例1

2.2 案例演示

2.2.1 案例1

package main

import "fmt"

func sum(num1 int, num2 int) int {
	defer fmt.Println(" num1 = ", num1) // 10
	defer fmt.Println(" num2 = ", num2) // 20

	// 增加两句话
	num1++ // 11
	num2++ // 21
	result := num1 + num2
	fmt.Println("result = ", result)
	return result
}

func main() {
	// result =  32
	// num2 =  20
	// num1 =  10
	// result_main =  32
	result_main := sum(10, 20)
	fmt.Println("result_main = ", result_main)
}

2.3 最佳实践

defer最主要的价值是 当函数执行完毕后,可以及时的释放函数创建的资源

2.3.1 案例2(模拟代码)

对文件进行操作,操作完成后关闭

func openFile(){
    file = openfile(文件名)
    defer file.close()
}

对数据库进行模拟操作

func connectDb(){
    connect = openDatabase()
    defer connect.close()
}
  1. 在golang中通常做法是,创建资源后,例(打开文件,获取数据库链接,获取锁资源)。可以执行 defer file.close() / defer connect.close()
  2. 在defer后,可以继续使用创建资源
  3. 当函数完毕后,系统会依次从defer栈中,取出语句,关闭资源
  4. 这种机制很简洁,程序员无需考虑关闭资源的时机