Go函数里面提供了defer关键字,可以注册多个延迟调用,这些调用以先进后出(FILO)的顺序在函数返回前被执行。这点有点类似java语言中异常处理中的的finaly子句,defer常用于保证一些资源最终一定能够得到回收和释放。
package mainfunc main() {//先进后出defer func() {println("first")}()defer func() {println("second")}()println("function body")
}function body
second
first
defer后面必须是函数或者方法的调用,否则会报错。
defer函数的实参在注册时通过值拷贝传递进去,下面示例的代码中,实参a额值在defer注册时通过值拷贝方式传递进去,后续语句a++并不会影响defer语句最后的输出结果。
func f() int {a := 0defer func(i int) {println("defer i=", i)}(a)a++return a
}//defer 打印结果
defer i=O
defer 语句必须先注册后才能执行,如果 defer 位于 return 之后,则defer因为没有注册,不会执行。
package mainfunc main() {defer func() {println("first")}()a := 0println(a)returndefer func() {println("second")}()
}0
first
主动调用os.Exit(int)退出进程时,defer将不在被执行(即使defer已经提前注册也不执行)
package mainimport "os"func main() {defer func() {println("defer")}()println("func body")os.Exit(1)
}func body
defer的好处是可以在一定程度上避免资源泄露,特别是有很多return语句,有多个资源需要关闭的场景中,很容易漏掉资源的关闭操作。例如:
func CopyFile (dst , src string) (w int64 , err error) {src, err := os.Open(src)if err != nil {return}dst, err := os.Create(dst)if err != nil {//src 很容 忘记关闭src.Close()return}w, err = io.Copy(dst,src)dst.Close()src.Close ()return
}
使用defer改写后,在打开资源无报错后直接调用defer关闭资源,一旦养成这种习惯,则很难会忘记资源的释放。例如:
func CopyFile (dst , src string) (w int64 , err error) {src, err := os.Open(src)if err != nil {return}defer src.Close()dst, err := os.Create(dst)if err != nil {//src 很容 忘记关闭returη}defer dst.Close()w, err = io.Copy(dst, src)return
}
defer也有明显的副作用:defer会推迟资源的释放,defer 尽量不要放到循环语句里面,将大部分函数内部的defer语句单独拆分成一个小函数是一种很好的实践方式。另外,defer相对于普通额函数调用需要间接的数据结构的支持,相当于对普通函数调用有一定的性能损耗。