前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go语言defer关键字

Go语言defer关键字

作者头像
会呼吸的Coder
发布2020-02-17 16:59:58
4820
发布2020-02-17 16:59:58
举报
文章被收录于专栏:会呼吸的Coder会呼吸的Coder

Go语言defer关键字

  • defer关键字用于延缓函数的执行
  • 只需要在调用普通函数或方法前加上关键字defer,就完成了defer所需要的语法。当defer语句被执行时,跟在defer后面的函数就会被延迟执行。直到包含该defer语句的函数执行完毕时,defer后面的函数才会被执行,不论包含defer语句的函数是通过return正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。
  • defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应该直接跟在请求资源的语句后。
  • 对文件的操作的例子
代码语言:javascript
复制
package ioutil
func ReadFile(filename string) ([]byte, error) {
    f, err := os.Open(filename)
    if err != nil  {
        return nil, err
    }
    defer f.Close()
    return ReadAll(f)
}
  • 处理互斥锁的例子
代码语言:javascript
复制
var mu sync.Mutex
var m = make(map[string]int)
func lookup(key string) int {
    mu.Lock()
    defer mu.Ulock()
    return m[key]
}
  • 调试复杂程序时,defer机制也常被用于记录何时进入和退出函数。
  • 下例中的bigSlowOperation函数,直接调用trace记录函数的被调情况。bigSlowOperation函数被调时,trace会返回一个函数值,该函数值会在bigSlowOperation退出时被调用。通过这种方式,我们可以只通过一条语句控制函数的入口和所有的出口,甚至可以记录函数的运行时间,如例子中的start。
代码语言:javascript
复制
func bigSlowOperation() {
    defer trace("bigSlowOPeration")()
    extra parentheses
    //lot of works
    time.Sleep(10 * time.Second) //simulate slow
    operation by sleeping
}

func trace(msg string) func() {
    start := time.Now()
    log.Printf("enter %s", msg)
    return func() {
        log.Printf("exit %s (%s)", msg, tie.Since(start))
    }
}
  • defer语句中的函数会在return语句更新返回值变量后再执行,又因为在函数定义的匿名函数可以访问该函数包括返回值变量内的所有变量,所以,对匿名函数采用defer机制,可以使其观察函数的返回值
代码语言:javascript
复制
func double(x int) (result int) {
    defer func() {fmt.Printf("double(%d) = %d\n", x, result)}()
    return x + x
}

_ = double(4) //"8"
  • 被延迟执行的匿名函数甚至可以修改函数返回给调用者的返回值
代码语言:javascript
复制
func triple(x int) (result int) {
    defer func() {return += x}()
    return double(x)
}

fmt.Println(triple(4))
  • 在循环体中的defer语句特别需要注意。因为只有在函数执行完毕后,这些被延迟的函数才会执行。
  • 下面的代码会导致系统的文件描述符耗尽,因为所有文件都被处理之前,没有文件会被关闭
代码语言:javascript
复制
for _, filename := range filenames {
    f, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer f.Close()
    descriptors
    //...
}
  • 一种方法是将循环体中的defer语句移至另外一个函数。在每次循环时,调用这个函数
代码语言:javascript
复制
for _, filename := range filenames {
    if err := doFile(filename); err != nil {
        return err
    }
}

func doFile(filename string) error {
    f, err := os.Open(filename)
    if err != nil {
        return err
    }
    
    defer f.Close()
    ...
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初级程序员 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Go语言defer关键字
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档