Golang 语言中的 defer 怎么使用?

2023-04-1910:02:06编程语言入门到精通Comments751 views字数 1970阅读模式

01文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

介绍文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

在 Golang 语言中,我们可以在函数(自定义和部分内置)或方法中使用 defer 关键字注册延迟调用(一个或多个),多个延迟调用的执行顺序是先进后出(FILO)。并且不会受到函数执行结束退出,显式调用 return 和主动(或被动)触发 panic 的影响,注册成功的所有延迟调用都会被执行,除非 defer 注册在 return 之后或者函数(或方法)调用 os.Exit(1)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

defer 注册多个延迟调用,执行顺序是先进后出(FILO)。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

示例代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

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 没有注册,将不会执行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

示例代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

func main () {
 fmt.Println("main")
 return
 defer func() {
  fmt.Println("A")
 }()
}

defer 所在的函数或方法中,如果调用 os.Exit(1),defer 即便注册,也不会执行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

示例代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

func main () {
 defer func() {
  fmt.Println("A")
 }()
 fmt.Println("main")
 os.Exit(1)
}

defer 必须在函数和方法中才可以使用,并且 defer 后面必须是函数(自定义和部分内置函数)或方法,defer 函数的实参是值拷贝。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

示例代码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

func main () {
 a := 0
 defer func(num int) {
  fmt.Println("defer func()", num)
 }(a)
 a++
 fmt.Println(a)
}

02文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

使用场景文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

使用关键字 defer 注册的函数(自定义和部分内置)或方法,因为不会受到函数执行结束,显式调用 return 和主动(或被动)触发 panic 的影响,通常会用于防止忘记释放资源和捕获 panic(同一 goroutine 中) 防止应用程序崩溃退出的应用场景。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

示例代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

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 会放在错误检查之后。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

示例代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

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 中),防止程序崩溃退出。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

03文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

注意事项文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

虽然使用 defer 具有可以用于防止忘记释放资源和拦截 panic(同一 goroutine 中)防止应用程序崩溃退出等好处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

但是 defer 也有副作用,它会使资源延迟释放,defer 尽量不要再 for-loop 中使用,并且相比于未使用 defer 调用的函数(自定义和部分内置)或方法,defer 也有一定的性能损耗,Golang 语言官方也在 golang 1.13 和 golang 1.14 中优化了 defer 的性能。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

相比于 defer 的性能损耗,defer 带来的使代码更加优雅、可读和健壮等优势,我认为 defer 综合来看,利大于弊,它可以给 gopher 们带来的收益比付出的代价更大。所以,我建议大家尽量使用 defer。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

还有一点需要注意的是,我们不要使用 defer 调用有返回值的自定义函数或方法,返回值会丢失,可能会给应用程序带来意想不到的错误。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

04文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

总结文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

本文我们介绍了 defer 的执行机制,使用场景和注意事项,并且给出了相应的示例代码。通常我们会在 Golang 语言开发中使用 defer 防止忘记释放资源(关闭文件或解锁)和捕获 panic(同一 goroutine 中) 防止应用程序崩溃退出。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/35801.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/ymba/35801.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定