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


目录
相关文章
|
2天前
|
测试技术 Go
go语言中测试工具
【10月更文挑战第22天】
10 4
|
2天前
|
SQL 关系型数据库 MySQL
go语言中数据库操作
【10月更文挑战第22天】
12 4
|
2天前
|
缓存 前端开发 中间件
go语言中Web框架
【10月更文挑战第22天】
13 4
|
5天前
|
Go
go语言的复数常量
【10月更文挑战第21天】
18 6
|
5天前
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
|
5天前
|
Go
go语言的浮点型常量
【10月更文挑战第21天】
13 4
|
5天前
|
编译器 Go
go语言的整型常量
【10月更文挑战第21天】
16 3
|
5天前
|
Serverless Go
Go语言中的并发编程:从入门到精通
本文将深入探讨Go语言中并发编程的核心概念和实践,包括goroutine、channel以及sync包等。通过实例演示如何利用这些工具实现高效的并发处理,同时避免常见的陷阱和错误。
|
6天前
|
安全 Go 开发者
代码之美:Go语言并发编程的优雅实现与案例分析
【10月更文挑战第28天】Go语言自2009年发布以来,凭借简洁的语法、高效的性能和原生的并发支持,赢得了众多开发者的青睐。本文通过两个案例,分别展示了如何使用goroutine和channel实现并发下载网页和构建并发Web服务器,深入探讨了Go语言并发编程的优雅实现。
18 2
|
2天前
|
安全 测试技术 Go
Go语言中的并发编程模型解析####
在当今的软件开发领域,高效的并发处理能力是提升系统性能的关键。本文深入探讨了Go语言独特的并发编程模型——goroutines和channels,通过实例解析其工作原理、优势及最佳实践,旨在为开发者提供实用的Go语言并发编程指南。 ####