golang特性
defer、recover异常捕获和处理
应用场景
1. 资源释放
- 文件操作:在打开文件后,使用 defer 关闭文件句柄,确保文件在函数退出时被关闭,避免资源泄漏。
- 数据库连接:在打开数据库连接后,使用 defer 关闭数据库连接,确保数据库连接在函数退出时被关闭,防止数据库连接泄漏。
- 锁释放:在加锁后,使用 defer 释放锁,确保在函数退出时锁被正确释放,避免死锁情况。
2. 异常捕获和处理
-
错误恢复:使用 defer 和 recover 捕 panic,并进行错误处理。在程序可能发生致命错误但不想使程序终止的情况下,可以使用 recover 恢复程序的执行,进行必要的错误处理后继续执行。
-
错误记录:在一些关键任务中,可能不想让程序崩溃,但又想记录异常情况。通过使用 defer 和 recover,可以在出现 panic
时记录错误信息,然后程序可以继续执行。
defer
defer 是 Go 语言中的一个关键字,用于延迟执行函数调用。被延迟执行的函数会在包含 defer 语句的函数返回之前执行,无论函数是正常返回还是发生了 panic。这使得 defer 可以用于一些清理操作,例如关闭文件、释放资源等。
- defer 关键词用来声明一个延迟调用函数,该函数可以是匿名函数也可以是具名函数
- defer延迟函数执行时间(位置),方法return之后,返回参数到调用方法之前
- defer 延迟函数可以在方法返回之后改变函数的返回值在方法结束(正常返回,异常结束)都会去调用
- defer声明的延迟函数,可以有效避免因异常导致的资源无法释放的问题 可以指定多个 defer延迟函数,多个延时函数执行顺序为后进先出
- defer 通常用于资源释放、异常捕获等场景,例如:关闭连接,关闭文件等
- defer 与recover 配合可以实现异常捕获与处理逻辑
- 不建议在for循环中使用defer
package main
import "fmt"
func main() {
for i := 0; i < 3; i++ {
defer fmt.Println("Deferred call in loop:", i)
}
fmt.Println("Loop ended")
}
Loop ended
Deferred call in loop: 2
Deferred call in loop: 1
Deferred call in loop: 0
defer 语句在循环结束后才执行,而且执行顺序是后进先出的。所以在循环中使用 defer 时要谨慎,确保不会导致不必要的资源浪费或不正确的行为。
defer 的典型用途包括:
关闭文件或数据库连接等资源释放
在函数返回之前记录日志信息
在发生异常时执行清理操作
在函数调用前后进行计时或性能分析等操作
recover
recover 是一个内建函数,用于从 panic 中恢复,并返回导致 panic 的值。它通常与 defer 语句一起使用,在发生 panic 时捕获异常,然后进行必要的处理。
- Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来
- recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果
- 如果当前的 goroutine 出现panic,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行
package main
import "fmt"
func recoverDemo() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}
func causePanic() {
defer recoverDemo() // 在panic发生后执行recoverDemo函数
fmt.Println("Start panic")
panic("panic occurred")
fmt.Println("End panic") // 这行代码不会被执行
}
func main() {
causePanic()
fmt.Println("Program continues") // 虽然有panic,但由于recover已经处理了,所以程序继续执行
}
输出结果
Start panic
Recovered: panic occurred
Program continues
当 causePanic 函数调用 panic 时,程序会发生 panic,但是由于 recoverDemo 函数被 defer 延迟调用,它在 panic 发生后被执行,打印出了 panic 的信息。然后程序继续执行,而不会终止。
panic
panic 是一个内建函数,用于表示程序遇到了一些无法处理的错误或不可恢复的情况,导致程序的执行无法继续下去。当程序遇到 panic 时,它会立即停止当前函数的执行,沿着函数调用栈向上一层层地执行延迟函数(即使用 defer 声明的函数),然后程序终止并打印出 panic 的信息。
- Go语言的一种异常机制
- 可通过panic 函数主动抛出异常
- 发生时停止函数执行:panic 会导致当前函数的执行立即停止,并开始执行 defer 声明的延迟函数。
- 传播至调用栈:如果当前函数无法处理 panic,它会向调用当前函数的函数传播 panic,直到遇到可以处理 panic 的 recover,或者直到达到最外层的函数,此时程序会终止并打印出 panic 的信息。
- 通常用于表示致命错误:panic 通常用于表示程序遇到了一些无法恢复的致命错误,例如无效的参数、内存溢出、文件不存在等。
package main
import "fmt"
func main() {
fmt.Println("Start of main")
panic("Something went wrong!") // 程序会在这里发生panic
fmt.Println("End of main") // 这行代码不会被执行
}
当 panic 被调用时,程序会立即停止执行 main 函数,并开始执行 defer 声明的延迟函数(如果有的话),然后打印出 panic 的信息并终止程序。
虽然 panic 可以用于表示程序的错误状态,但通常建议只在遇到无法处理的情况下才使用 panic,而在可以预见和处理的错误情况下使用错误类型进行错误处理。