Go语言defer关键字

幸运草 2020年4月22日22:14:08函数代码评论阅读模式

Go语言defer关键字

  • defer关键字用于延缓函数的执行
  • 只需要在调用普通函数或方法前加上关键字defer,就完成了defer所需要的语法。当defer语句被执行时,跟在defer后面的函数就会被延迟执行。直到包含该defer语句的函数执行完毕时,defer后面的函数才会被执行,不论包含defer语句的函数是通过return正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。
  • defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应该直接跟在请求资源的语句后。
  • 对文件的操作的例子
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)
}
  • 处理互斥锁的例子
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。
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机制,可以使其观察函数的返回值
func double(x int) (result int) {
defer func() {fmt.Printf("double(%d) = %dn", x, result)}()
return x + x
}

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

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

特别声明:以上文章内容仅代表作者本人观点,不代表变化吧观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。

  • 赞助本站
  • 微信扫一扫
  • weinxin
  • 加入Q群
  • QQ扫一扫
  • weinxin
幸运草
Go语言中的常量 函数代码

Go语言中的常量

1 概述 常量,一经定义不可更改的量。功能角度看,当出现不需要被更改的数据时,应该使用常量进行存储,例如圆周率。从语法的角度看,使用常量可以保证数据,在整个运行期间内,不会被更改。例如当前处理器的架构...
Go语言的接口 函数代码

Go语言的接口

Go语言-接口 在Go语言中,一个接口类型总是代表着某一种类型(即所有实现它的类型)的行为。一个接口类型的声明通常会包含关键字type、类型名称、关键字interface以及由花括号包裹的若干方法声明...
Go语言支持的正则语法 函数代码

Go语言支持的正则语法

1 字符 语法 说明 . 任意字符,在单行模式(s标志)下,也可以匹配换行 字符类 否定字符类 d Perl 字符类 D 否定 Perl 字符类 ASCII 字符类 否定 ASCII 字符类 pN U...
Go语言的包管理 函数代码

Go语言的包管理

1 概述 Go 语言的源码复用建立在包(package)基础之上。包通过 package, import, GOPATH 操作完成。 2 main包 Go 语言的入口 main() 函数所在的包(pa...

发表评论