defer

为什么需要defer?

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

快速入门案例:

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 将语句放入到栈时,也会将相关的值拷贝同时入栈。

案例:

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
  //增加一句话
  n1++   
  n2++   
  res := n1 + n2   
  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 最主要的价值是在,当函数执行完毕后,可以及时的释放函数创建的资源。

说明:

  1)在golang编程中的通常做法是,创建资源后,比如(打开了文件句柄,获取了数据库的连接,或者是锁资源),可以执行 defer file.Colse()  和 defer connect.Close()。

  2)在 defer 后,可以继续使用创建资源。

  3)当函数完毕后,系统会依次从 defer 栈中,取出语句,关闭资源。

  4)这种机制非常简洁,程序员不用再为在什么时机关闭资源而烦心了。

原文地址:https://www.cnblogs.com/green-frog-2019/p/11355127.html