Golang 语言中的 Defer 怎么使用?

开发 后端
在 Golang 语言中,我们可以在函数(自定义和部分内置)或方法中使用 defer 关键字注册延迟调用(一个或多个),多个延迟调用的执行顺序是先进后出(FILO)。

[[404327]]

 01介绍

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

defer 注册多个延迟调用,执行顺序是先进后出(FILO)。

示例代码:

  1. func main () { 
  2.  defer func() { 
  3.   fmt.Println("A"
  4.  }() 
  5.  
  6.  defer func() { 
  7.   fmt.Println("B"
  8.  }() 
  9.  
  10.  fmt.Println("main goroutine run over"
  11.  
  12.  // panic("this is a panic example"
  13.  
  14.  // return 

defer 如果定义在 return 之后,它等于 defer 没有注册,将不会执行。

示例代码:

  1. func main () { 
  2.  fmt.Println("main"
  3.  return 
  4.  defer func() { 
  5.   fmt.Println("A"
  6.  }() 

defer 所在的函数或方法中,如果调用 os.Exit(1),defer 即便注册,也不会执行。

示例代码:

  1. func main () { 
  2.  defer func() { 
  3.   fmt.Println("A"
  4.  }() 
  5.  fmt.Println("main"
  6.  os.Exit(1) 

defer 必须在函数和方法中才可以使用,并且 defer 后面必须是函数(自定义和部分内置函数)或方法,defer 函数的实参是值拷贝。

示例代码

  1. func main () { 
  2.  a := 0 
  3.  defer func(num int) { 
  4.   fmt.Println("defer func()", num) 
  5.  }(a) 
  6.  a++ 
  7.  fmt.Println(a) 

02使用场景

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

示例代码:

  1. func main () { 
  2.  f, err := os.OpenFile("text.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755) 
  3.  if err != nil { 
  4.   fmt.Println(err) 
  5.  } 
  6.  defer f.Close() 
  7.  n, err := f.WriteString("this is a text file\t"
  8.  if err != nil { 
  9.   fmt.Println(err) 
  10.  } 
  11.  fmt.Println(n) 

阅读上面这段代码,我们使用 defer 延迟调用释放资源,防止忘记释放资源(关闭文件或解锁),通常 defer 会放在错误检查之后。

示例代码:

  1. func main () { 
  2.  defer func() { 
  3.   if err := recover(); err != nil { 
  4.    fmt.Println("this is a panic" ) 
  5.   } 
  6.  }() 
  7.  panic("this is a test panic"
  8.  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 中) 防止应用程序崩溃退出。

本文转载自微信公众号「Golang语言开发栈」,可以通过以下二维码关注。转载本文请联系Golang语言开发栈公众号。

 

责任编辑:武晓燕 来源: Golang语言开发栈
相关推荐

2013-06-25 09:52:32

GoGo语言Go编程

2021-04-28 09:02:48

Golang语言Context

2024-01-07 23:11:16

defer​Go语言

2021-09-13 05:02:49

GogRPC语言

2024-04-01 00:02:56

Go语言代码

2021-07-12 05:05:59

Golang语言字段

2021-01-29 08:56:13

Golang标准库函数

2021-06-09 23:36:46

Golang语言版本

2021-05-07 15:28:03

Kafka客户端Sarama

2011-05-13 17:25:34

C

2024-05-10 08:36:40

Go语言对象

2021-06-29 23:40:19

Golang语言并发

2021-12-13 01:24:14

语言Golang panic

2021-10-10 23:02:49

Golang语言代码

2011-05-25 13:22:05

PHPJSON

2014-04-09 09:32:24

Go并发

2022-01-04 23:13:57

语言PanicGolang

2021-07-26 11:19:43

微服务开发技术

2021-11-08 23:09:07

Go排序数据

2023-10-09 07:14:42

panicGo语言
点赞
收藏

51CTO技术栈公众号