Go 语言的 panic 和 recover

Go 语言的 panic 和 recover 是两种用于处理异常的机制。

它们允许在程序运行过程中进行错误处理,特别是处理那些不可恢复的错误和恢复程序的执行。

panic 和 recover 并不像其他语言中的 try-catch 机制那样常见,而是提供了控制程序流程的一种方法。

  1. panic 机制

panic 用于表示程序遇到了一个不可恢复的错误,或者程序处于一个不应继续执行的状态。它会触发运行时的异常,并停止当前函数的执行,直到调用 recover 或程序退出。

1.1 panic 的使用

当你遇到一个严重的错误或不合法的操作时,可以调用 panic 来终止当前函数的执行,并开始触发一系列的 defer 函数。

panic 会导致当前函数的执行停止,并且会递归地向上层函数传播,直到程序结束或者有 recover 恢复它。

使用 panic 终止程序

go 复制代码
func divide(a, b int) int {
	if b == 0 {
		panic("division by zero") // 如果除数为零,抛出 panic
	}
	return a / b
}

func main() {
	fmt.Println("Starting program")

	// 这里会触发 panic
	result := divide(10, 0)

	fmt.Println("Result:", result) // 这行代码不会执行
}
当 b == 0 时,panic 被触发,程序会停止当前函数的执行,并开始递归传播。

panic 的错误信息是 "division by zero",这将显示在控制台。

调用 panic 后,程序停止执行,任何后续代码都不会被执行,除非在调用链中有 recover。
  1. recover 机制

recover 是 Go 语言中用来从 panic 中恢复的函数。它只能在 defer 中使用。当 recover 被调用时,它会捕获到正在执行的 panic,并停止 panic 继续传播,恢复程序的执行。

2.1 recover 的使用

recover 会返回 panic 时传递的值,并恢复程序的正常执行。

如果 recover 没有在 panic 发生时被调用,panic 会继续向上传播,直到程序退出。

只有在 defer 函数内部,recover 才能捕获到 panic。

使用 recover 恢复程序

go 复制代码
func divide(a, b int) int {
	if b == 0 {
		panic("division by zero") // 如果除数为零,抛出 panic
	}
	return a / b
}

func safeDivide(a, b int) (result int, err string) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Sprintf("Recovered from panic: %v", r)
		}
	}()

	result = divide(a, b) // 可能会触发 panic
	return
}

func main() {
	result, err := safeDivide(10, 0)
	if err != "" {
		fmt.Println("Error:", err)
	} else {
		fmt.Println("Result:", result)
	}
}
Error: Recovered from panic: division by zero
safeDivide 函数尝试执行 divide,如果 divide 触发了 panic,recover 会捕获到并停止 panic 继续传播。

defer 中的匿名函数通过 recover() 捕获 panic,并将 panic 的信息作为错误返回。

程序恢复执行并返回错误信息,而不会崩溃。
  1. panic 和 recover 的工作流程

panic 的传播:

当 panic 被触发时,Go 会立即停止当前函数的执行,并开始递归地向上传播 panic。

每次 panic 传播时,都会执行当前函数的所有 defer 语句。

recover 捕获 panic:

如果在某个 defer 语句中调用 recover,它将会捕获当前的 panic 并停止 panic 的传播。

recover 返回 panic 时传递的参数,通常是一个错误信息。

如果没有 recover 捕获,panic 将继续传播,直到程序退出。

  1. panic 和 recover 使用的注意事项

不要滥用 panic 和 recover:Go 语言的设计原则鼓励使用显式的错误处理机制(例如返回错误值),而不是通过 panic 来表示常规错误。panic 应该只用于处理那些程序无法恢复的错误(例如数组越界、无法恢复的逻辑错误等)。

defer 和 recover:recover 必须在 defer 函数中调用才能有效。如果 recover 不在 defer 中,它将无法捕获到 panic。

panic 和 recover 用于同步代码:panic 和 recover 通常用于同步函数的错误处理。它们不能用于并发的错误处理(例如 goroutine 中的错误处理)。对于并发程序,Go 更倾向于通过返回错误值来处理错误。

panic 和 recover 的机制引入了较高的开销,通常应避免在高频调用的代码中使用 panic。它们更适合处理那些异常、不可恢复的错误,而不是正常的程序流程控制。

  1. 实际应用中的使用场景

panic 的场景:当程序遇到无法继续执行的严重错误时,调用 panic。例如:空指针解引用、数组越界等。

recover 的场景:当你希望在程序中捕获并恢复某些不可预见的错误时,使用 recover。常见的场景包括:Web 服务器的中间件处理、数据库事务的回滚等。

Web 服务器的 panic 恢复

go 复制代码
func handler(w http.ResponseWriter, r *http.Request) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered from panic:", r)
			http.Error(w, "Internal server error", http.StatusInternalServerError)
		}
	}()

	panic("Something went wrong") // 模拟一个 panic
}


func main() {
	http.HandleFunc("/", handler)
	fmt.Println("Starting server at :8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		fmt.Println("Server failed:", err)
	}
}

服务器启动:当你运行程序时,main 函数启动 HTTP 服务器并监听端口 8080。

触发 panic:当你访问根路径(http://localhost:8080/)时,handler 函数会被触发。在 handler 函数中,程序会故意执行 panic("Something went wrong"),模拟一个错误的发生。

recover 捕获 panic:panic 触发后,程序会立即停止当前函数的执行。此时,defer 语句中的匿名函数被调用,recover 会捕获到 panic,然后通过 http.Error 返回 500 错误响应给客户端。

恢复并继续执行:由于 recover 捕获并处理了 panic,程序不会崩溃,而是继续执行。
相关推荐
源代码•宸2 小时前
分布式缓存-GO(分布式算法之一致性哈希、缓存对外服务化)
开发语言·经验分享·分布式·后端·算法·缓存·golang
云和数据.ChenGuang2 小时前
PHP-FPM返回的File not found.”的本质
开发语言·php·运维工程师·运维技术
R.lin2 小时前
Java 8日期时间API完全指南
java·开发语言·python
yangpipi-2 小时前
《C++并发编程实战》 第4章 并发操作的同步
开发语言·c++
火钳游侠3 小时前
java单行注释,多行注释,文档注释
java·开发语言
有趣的我3 小时前
C++ 多态介绍
开发语言·c++
fie88893 小时前
波束赋形MATLAB代码实现
开发语言·matlab
丘狸尾3 小时前
gradio uv无法add
开发语言·python
sali-tec4 小时前
C# 基于halcon的视觉工作流-章67 深度学习-分类
开发语言·图像处理·人工智能·深度学习·算法·计算机视觉·分类
全栈陈序员4 小时前
【Python】基础语法入门(十七)——文件操作与数据持久化:安全读写本地数据
开发语言·人工智能·python·学习