介绍
在 Golang 语言中,我们可以在函数(自定义和部分内置)或方法中使用 defer 关键字注册延迟调用(一个或多个),多个延迟调用的执行顺序是先进后出(FILO)。并且不会受到函数执行结束退出,显式调用 return 和主动(或被动)触发 panic 的影响,注册成功的所有延迟调用都会被执行,除非 defer 注册在 return 之后或者函数(或方法)调用 os.Exit(1)
。
defer 注册多个延迟调用,执行顺序是先进后出(FILO)。
示例代码:
func main () { defer func() { fmt.Println("A") }() defer func() { fmt.Println("B") }() fmt.Println("main goroutine run over") // panic("this is a panic example") // return }
defer 如果定义在 return 之后,它等于 defer 没有注册,将不会执行。
示例代码:
func main () { fmt.Println("main") return defer func() { fmt.Println("A") }() }
defer 所在的函数或方法中,如果调用 os.Exit(1)
,defer 即便注册,也不会执行。
示例代码:
func main () { defer func() { fmt.Println("A") }() fmt.Println("main") os.Exit(1) }
defer 必须在函数和方法中才可以使用,并且 defer 后面必须是函数(自定义和部分内置函数)或方法,defer 函数的实参是值拷贝。
示例代码
func main () { a := 0 defer func(num int) { fmt.Println("defer func()", num) }(a) a++ fmt.Println(a) }
02
使用场景
使用关键字 defer 注册的函数(自定义和部分内置)或方法,因为不会受到函数执行结束,显式调用 return 和主动(或被动)触发 panic 的影响,通常会用于防止忘记释放资源和捕获 panic(同一 goroutine 中) 防止应用程序崩溃退出的应用场景。
示例代码:
func main () { f, err := os.OpenFile("text.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755) if err != nil { fmt.Println(err) } defer f.Close() n, err := f.WriteString("this is a text file\t") if err != nil { fmt.Println(err) } fmt.Println(n) }
阅读上面这段代码,我们使用 defer 延迟调用释放资源,防止忘记释放资源(关闭文件或解锁),通常 defer 会放在错误检查之后。
示例代码:
func main () { defer func() { if err := recover(); err != nil { fmt.Println("this is a panic" ) } }() panic("this is a test panic") fmt.Println("main") }
阅读上面这段代码,我们使用 defer 配合 recover 函数,用于拦截 panic(同一 goroutine 中),防止程序崩溃退出。
03
注意事项
虽然使用 defer 具有可以用于防止忘记释放资源和拦截 panic(同一 goroutine 中)防止应用程序崩溃退出等好处。
但是 defer 也有副作用,它会使资源延迟释放,defer 尽量不要再 for-loop 中使用,并且相比于未使用 defer 调用的函数(自定义和部分内置)或方法,defer 也有一定的性能损耗,Golang 语言官方也在 golang 1.13 和 golang 1.14 中优化了 defer 的性能。
相比于 defer 的性能损耗,defer 带来的使代码更加优雅、可读和健壮等优势,我认为 defer 综合来看,利大于弊,它可以给 gopher 们带来的收益比付出的代价更大。所以,我建议大家尽量使用 defer。
还有一点需要注意的是,我们不要使用 defer 调用有返回值的自定义函数或方法,返回值会丢失,可能会给应用程序带来意想不到的错误。
04
总结
本文我们介绍了 defer 的执行机制,使用场景和注意事项,并且给出了相应的示例代码。通常我们会在 Golang 语言开发中使用 defer 防止忘记释放资源(关闭文件或解锁)和捕获 panic(同一 goroutine 中) 防止应用程序崩溃退出。
关于 defer 的原理分析,限于篇幅,并没有花费笔墨,感兴趣的读者朋友们,建议自行搜索相关资料了解相关内容。
推荐阅读:
参考资料:
https://gobyexample-cn.github.io/defer
https://blog.golang.org/defer-panic-and-recover