golang defer学习与思考

网上内容摘录学习

defer基础使用

此段落参考 https://my.oschina.net/chai2010/blog/140065

  1. 资源回收,比如file,mutex
f, err := os.Open("file")
if err != nil {
      panic(err)
}
defer f.Close()
mu.Lock()
defer mu.Unlock()
  1. panic的捕获
defer func() { 
      if r := recover(); r != nil { 
         fmt.Println("Recovered in f", r) 
      }
}()
  1. 返回值的修改
func F() (i int) {
     defer func() {
        i++
     }()
     return 1
}   // 返回值为2

defer的实际调用时机

此段落参考 https://tiancaiamao.gitbooks.io/go-internals/content/zh/03.4.html

defer是在return之前执行的。

这是官方原话说的,实际上,
这个return应该分三个步骤:

返回值 = xxx
调用defer函数
空的return

举个例子:

func f() (i int) {
     j := 1
     defer func() {
       j = j + 1
     }()
     return j
}
  • i = j
  • j = j + 1
  • return
    所以,函数的返回值实际是1

defer三大特性

此段落参考 https://my.oschina.net/chai2010/blog/119216

  1. 当defer调用函数的时候, 函数用到的每个参数和变量的值也会被计算
    在这个例子中, 表达式"i"的值将在defer fmt.Println(i)
    时被计算. Defer将会在 当前函数返回的时候打印"0"
func a() {
      i := 0 
      defer fmt.Println(i) 
      i++ 
      return
}
  1. Defer调用的函数将在当前函数返回的时候, 以后进先出的顺序执行.
    下面的函数将输出"3210":
func b() {
      for i := 0; i < 4; i++ {
          defer fmt.Print(i) 
      }
}
  1. Defer调用的函数可以在返回语句执行后读取或修改命名的返回值.
    在这个例子中, defer语句将会在当前函数返回后将i增加1. 实际上, 函数会返回2:
func c() (i int) {
      defer func() {
          i++ 
      }() 
      return 1
}

学习后的思考

主要是对特性1的思考

当defer调用函数的时候, 函数用到的每个参数和变量的值也会被计算

主要是考虑,指针传入的情况
直接上代码:

func F1() {
   var a *int
   var c int = 6
   a = &c
   defer func(a *int) {
      fmt.Println(*a)
   }(a)
   var b int = 5
   a = &b
}  // 此时打印的是:6
  • 此段代码中,defer调用时,a指针指向的是c的地址,
    后面再改变a的地址,defer中已经存起来的a的地址都不会改变了。
    相当于a把地址拷贝给了defer中的备份a。
func F2() {
   var a *int
   var c int = 6
   a = &c
   defer func() {
      fmt.Println(*a)
   }()
   var b int = 5
   a = &b
}  // 此时打印的是:5
  • 此段代码中,defer直接使用了a的地址,是a在return时候,所指向的实际地址。
func F3() {
   var a int = 0
   defer func(a *int) {
      fmt.Println(*a)
   }(&a)
   a = 5
}  // 此时打印的是:5
  • 此段代码中,传入了a的地址,后面修改该地址上的值,是会起作用的。

另外两个小点:

  1. defer是会稍微消耗性能,大概100~200ns
  2. defer在panic的情况下,也会调用的

补充:
在学习effect go时,学习到:
defer中参数是函数时,该函数会先执行

func main() {
   T11()
}
func T11() {
   s := "a"
   defer T12(T13(s))
   fmt.Println("T11", s)
}
func T12(s string) string {
   fmt.Println("T12", s)
   return s
}
func T13(s string) string {
   fmt.Println("T13", s)
   return s
}

// 结果
T13 a
T11 a
T12 a

你可能感兴趣的:(golang defer学习与思考)