当我们开始学习 Go 时,defer 语句可能是我们最先发现非常有趣的事情之一
defer 语句实际上有 3 种类型(截至 Go 1.22,但以后可能会发生变化):
- open-coded defer
- heap-allocated defer
- stack-allocated defer
每种类型都有不同的性能和最佳使用场景,如果你想优化性能,了解这些类型会很有用。
什么是 defer?
在深入探讨之前,让我们先快速了解一下 defer。
在 Go 中,defer是一个关键字,用于defer某个函数的执行,直到周围函数完成。
func main() { |
在此代码片段中,defer 语句计划fmt.Println("hello")在函数的最后执行main。因此,fmt.Println("world")会立即调用,并首先打印“world”。
之后,由于我们使用了 defer,因此在main完成之前的最后一步会打印“hello”。
这就像在函数退出之前设置稍后运行的任务一样。
这对于清理操作非常有用,例如关闭数据库连接、释放互斥锁或关闭文件:
func doSomething() error { |
上面的代码很好地展示了 defer 的工作原理,但这也是一种糟糕的使用方式。
Defer 被堆叠
defer当您在函数中使用多个语句时,它们将按照“堆栈stack”顺序执行,这意味着最后一个defer函数将首先执行。
func main() { |
- 每次调用 defer 语句时,都会将该函数添加到当前 goroutine 链接列表的顶部
- 当函数返回时,它会遍历链接列表并按照上图所示的顺序执行每个函数。
但请记住,它不会执行 goroutine 链表中的所有 defer,它只会运行返回函数中的 defer,因为我们的 defer 链表可能包含来自许多不同函数的许多 defer。
func B() { |
因此,只执行当前函数(或当前堆栈框架)中的defer函数。
但是有一种典型的情况是,当前 goroutine 中的所有defer函数都会被跟踪和执行,这时就会发生panic。
Defer, Panic 和 Recover
除了编译时错误外,我们还遇到许多运行时错误:除以零(仅限整数)、越界、取消引用零指针等等。这些错误会导致应用程序崩溃。
Panic 是一种停止当前 goroutine 执行、展开堆栈并执行当前 goroutine 中的defer函数的方法,从而导致我们的应用程序崩溃。
为了处理意外错误并防止应用程序崩溃,您可以使用recover defer函数中的函数来重新获得对恐慌 goroutine 的控制权。
func main() { |
通常,人们在 panic 中放置一个错误并用 捕获它recover(..),但它可以是任何东西:字符串、整数等。
在上面的例子中,defer函数内部是唯一可以使用的地方recover。让我进一步解释一下。
这里我们可以列出几个错误。我在实际代码中至少见过三个这样的代码片段。
详细点击标题