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 的用法了。如还有补充,请告知。


目录
相关文章
|
6天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
4天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
13 2
|
4天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
16 2
|
8天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
85 58
|
7天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
4天前
|
Go
go语言中的 跳转语句
【11月更文挑战第4天】
12 4
|
4天前
|
JSON 安全 Go
Go语言中使用JWT鉴权、Token刷新完整示例,拿去直接用!
本文介绍了如何在 Go 语言中使用 Gin 框架实现 JWT 用户认证和安全保护。JWT(JSON Web Token)是一种轻量、高效的认证与授权解决方案,特别适合微服务架构。文章详细讲解了 JWT 的基本概念、结构以及如何在 Gin 中生成、解析和刷新 JWT。通过示例代码,展示了如何在实际项目中应用 JWT,确保用户身份验证和数据安全。完整代码可在 GitHub 仓库中查看。
18 1
|
8天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
8天前
|
Go 数据处理 调度
探索Go语言的并发模型:Goroutines与Channels的协同工作
在现代编程语言中,Go语言以其独特的并发模型脱颖而出。本文将深入探讨Go语言中的Goroutines和Channels,这两种机制如何协同工作以实现高效的并发处理。我们将通过实际代码示例,展示如何在Go程序中创建和管理Goroutines,以及如何使用Channels进行Goroutines之间的通信。此外,本文还将讨论在使用这些并发工具时可能遇到的常见问题及其解决方案,旨在为Go语言开发者提供一个全面的并发编程指南。
|
6天前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。