Go语言编程的一大杀器!详解defer语句

简介: Go语言编程的一大杀器!详解defer语句

/ Go 语言 defer 详解 /

defer 语句可以延迟函数执行,在 Go 语言中用法非常广泛。本文将全面详细介绍 defer 的用法。主要内容包括

  1. defer 基本语法
  2. defer 执行时机
  3. defer 执行顺序
  4. defer 经典案例
  5. defer 捕获参数值
  6. return 后定义的 defer
  7. defer 语句注意事项
  8. defer 实现原理
  9. defer 性能分析


1

 

一、defer 基本语法

defer 语句的语法格式:

defer 表达式

表达式可以是一个函数调用,也可以是读取变量等语句。例如:

func main() {
  defer fmt.Println("deferred")
  defer func() {
    fmt.Println("deferred anonymous") 
  }()
}

defer 后跟具体要延迟执行的语句。


2

 

二、defer 执行时机

defer 表达式会在函数结束前执行。

比如正常返回:

func main() {
  fmt.Println("start")
  defer fmt.Println("deferred")
  fmt.Println("end")
  return 
}

打印顺序是:start -> end -> deferred

defer 表达式在 return 前执行。

如果函数中有错误导致提前退出:

func main() {
  fmt.Println("start")
  defer fmt.Println("deferred")  
  panic("crash")
}

打印顺序是:start -> deferred -> crash

虽然 panic 导致退出,defer 依然执行。


3

 

三、defer 执行顺序

多个 defer 语句会按照先进后出(FILO)的顺序执行:

func main() {
  for i := 0; i < 3; i++ {
    defer fmt.Println(i)
  }
}

打印结果是:2 -> 1 -> 0

后定义的先执行,类似栈结构。

也可以在 defer 中修改共享变量控制执行顺序:

func main() {
  for i := 0; i < 3; i++ {
    defer func(n int) {
      fmt.Println(n)
    }(i)
  }
}

这样会按照 0 -> 1 -> 2 的顺序打印。


4

 

四、defer 经典案例

defer 经常用于处理资源释放、解锁、文件关闭等操作。

4.1

 

1. 文件关闭

func main() {
  f, _ := os.Open("/tmp/file.txt")
  defer f.Close()
  // 操作文件 
} 

这样可以保证文件句柄释放。

4.2

 

2. 解锁

var mu sync.Mutex
func main() {
  mu.Lock()
  defer mu.Unlock()
  // 共享资源操作
}

在 return 前解锁。

4.3

 

3. 资源清理

func main() {
  cfg := initConfig()
  defer cfg.Cleanup()
  // 使用配置
  // ...
}

不管有没有错误,资源都会被清理。


5

 

五、defer 捕获参数值

defer 表达式中的函数,会立即使用当前参数的拷贝:

func main() {
  i := 0
  defer fmt.Println(i) // 拷贝值
  i++ // 对i修改无效
}

打印结果为 0,修改 i 值不影响 defer 表达式。


6

 

六、return 后定义的 defer

return 语句后定义的 defer 不会执行:

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

"finished"不会被打印,因为 defer 在 return 后定义。


7

 

七、defer 语句注意事项

使用 defer 语句时需要注意:

  • 参数值被拷贝捕获
  • return 后定义的 defer 不执行
  • 性能开销较大时避免使用
  • 过多 defer 嵌套会导致性能问题


8

 

八、defer 实现原理

defer 通过将语句加入调用帧(goroutine 栈)实现延迟执行。

简化的流程如下:

// 延迟调用对象
type defer struct {
  fn func()
  frame *frame
}
// 当调用函数时 
1. 在帧中添加 defer对象
2. 函数结束前,按FILO顺序调用defer.fn()

可以看到延迟调用其实就是通过帧记录并在 return 前执行。


9

 

九、defer 性能分析

defer 语句可以简化代码逻辑,但也会有一定性能损耗。

优化方式:

  • 避免在循环内使用 defer
  • 少用 defer 嵌套(超过 2 层)
  • 避免大量垃圾对象在 defer 中创建
  • 用同步原语替代等

建议通过测试确定是否需要优化。


10

 

总结

defer 是 Go 语言非常实用的语句,可以大大简化资源管理等代码。但也需要注意使用效率。

通过本文详细的介绍,相信你可以很好掌握 defer 的用法了。如还有补充,请告知。


目录
相关文章
|
4月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
276 1
|
6月前
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
381 1
|
6月前
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
464 0
|
6月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
313 0
|
6月前
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
339 0
|
6月前
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
374 0
|
6月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
6月前
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
7月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
7月前
|
Go
如何在Go语言的HTTP请求中设置使用代理服务器
当使用特定的代理时,在某些情况下可能需要认证信息,认证信息可以在代理URL中提供,格式通常是:
514 0