zoukankan      html  css  js  c++  java
  • 『go成长之路』 defer 作用、典型用法以及多个defer调用顺序,附加defer避坑点,拿来吧你

    预习内容

    • defer 的作用有哪些?
    • 多个 defer 的执行顺序是怎样的?
    • defer,return,函数返回值 三者之间的执行顺序

    defer的作用

    go中的defer延迟函数,一般是用于释放资源或者收尾工作

    由于defer是具有延迟特性且执行动作是在函数return之后,因此作为资源释放作用再好不过。

    • 典型例子:释放锁、关闭文件、关闭链接
    // 释放锁
    func getValue() {
      s.Lock()
      defer s.Unlock()
      ...
    }
    
    // 关闭文件
    func read() {
      f, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
    	defer f.Close()
      ...
    }
    
    // 关闭链接
    func connect() {
      resp, err := grequests.Get(s.cfg.GateApiCrossMarginUrl, nil)
    	defer resp.Close()
      ...
    }
    
    // 收尾工作,defer 同时也是函数,可以做很多收尾相关工作
    func closeConnection() {
      ...
      defer func() {
        file.close()
        close(readChan)
      }
      ...
    }
    
    • 还有作用就是捕获 panic,这个功能在defer里也是典型用法
    func sendChan() {
      // 此处捕获 panic 进行 recover 防止程序崩溃
    	defer func() {
        if ok := recover(); ok != nil {
            fmt.Println("recover")
        }
      }()
      // 向已经关闭的chan发送数据,此处会引起 panic
      dataChan <- "message"
      ...
    }
    

    defer 释放资源『避坑指南』

    资源释放动作一定紧跟资源使用(打开、连接)语句,不然defer可能不会被执行到,导致内存泄露

    // 关闭文件
    func read() error {
      f1, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
      if err != nil {
        return err
      }
      // 此时,defer还没执行到,提前return了,无效defer导致内存泄露
    	defer f1.Close()
      
      // 正确用法,紧跟资源使用语句
      f2, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
      defer f2.Close()
      if err != nil {
        return err
      }
    }
    

    defer 的调用顺序

    墙裂建议不要先看后面的介绍做下面的题目,此处先跳过介绍defer调用顺序,看看下面比较典型的对defer顺序判断,你能看出来几个?

    func deferFunc1(i int) (t int) {
    	t = i
    	defer func() {
    		t += 1
    	}()
    	return t
    }
    
    func deferFunc2(i int) int {
    	t := i
    	defer func() {
    		t += 1
    	}()
    	return t
    }
    
    func deferFunc3(i int) (t int) {
    	defer func() {
    		t += i
    	}()
    	return 1
    }
    
    func deferFunc4() (t int) {
    	defer func(i int) {
    		fmt.Println(i)
    		fmt.Println(t)
    	}(t)
      t = 0
    	return 1
    }
    
    func ExecDeferFunc() {
      // 猜猜下面输出的内容和顺序
    	fmt.Println(deferFunc1(1)) 
    	fmt.Println(deferFunc2(1))
    	fmt.Println(deferFunc3(1))
    	deferFunc4()
    }
    

    猜想结果可能是:2,2,1,0,0 或者 2,2,1,1,1 ?

    估计比较模糊的地方应该是函数返回值 和 return value(函数返回值)关系不明确,还有就是对defer产生作用的时机不明确

    正文开始!

    再次强调defer是延迟函数,执行动作在return之后,defer相当于是将执行动作压入栈中,越是后面的defer越是先执行,执行顺序是LIFO(后进先出)

    特别指出:有函数返回值的则return将结果写入返回值,defer进行收尾,可以看做 return最先执行,然后return将结果存入返回值,最后defer执行

    那么基于刚刚的介绍再回头去看得到的『实际结果是:2,1,2,0,1』

    复习内容

    • defer 用于资源释放和收尾工作
    • 多个 defer 调用顺序是 LIFO(后入先出),defer后的操作可以理解为压入栈中
    • defer,return,return value(函数返回值) 执行顺序:首先return,其次return value,最后defer。defer可以修改函数最终返回值。
  • 相关阅读:
    读写锁操作(ReaderWriterLockSlim)
    VirtualBox的小秘密:命令行
    云的始祖概念,认识Linux瘦客户机
    Flash ActionScript 3.0 通过asp.net 访问 数据库
    js刷新iframe框架的几种情况分析
    Mozilla两款火狐插件包含恶意代码被紧急喊停
    asp.net中DataBinder.Eval的用法总结
    实现firebird的Embedded模式(.net 3.5)
    Flash Player 9 支持H.264视频和aac音频(附官方代码)
    右下角浮动广告代码DEMO
  • 原文地址:https://www.cnblogs.com/sinclairni/p/15126506.html
Copyright © 2011-2022 走看看