942 lines
21 KiB
Go
942 lines
21 KiB
Go
/*
|
||
03-panic-recover.go - Go 语言 Panic 和 Recover 详解
|
||
|
||
学习目标:
|
||
1. 理解 panic 的概念和使用场景
|
||
2. 掌握 recover 的恢复机制
|
||
3. 学会 defer 与 panic/recover 的配合
|
||
4. 了解 panic 的传播机制
|
||
5. 掌握 panic/recover 的最佳实践
|
||
|
||
知识点:
|
||
- panic 的触发条件和行为
|
||
- recover 的恢复机制
|
||
- defer 语句的执行顺序
|
||
- panic 的传播和栈展开
|
||
- panic/recover 的实际应用
|
||
- 错误处理 vs panic 的选择
|
||
*/
|
||
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"runtime"
|
||
"strconv"
|
||
"time"
|
||
)
|
||
|
||
func main() {
|
||
fmt.Println("=== Go 语言 Panic 和 Recover 详解 ===\\n")
|
||
|
||
// 演示 panic 的基本概念
|
||
demonstratePanicBasics()
|
||
|
||
// 演示 recover 的使用
|
||
demonstrateRecover()
|
||
|
||
// 演示 defer 与 panic/recover 的配合
|
||
demonstrateDeferWithPanicRecover()
|
||
|
||
// 演示 panic 的传播
|
||
demonstratePanicPropagation()
|
||
|
||
// 演示 panic/recover 的实际应用
|
||
demonstratePracticalUsage()
|
||
|
||
// 演示最佳实践
|
||
demonstrateBestPractices()
|
||
|
||
// 演示错误处理 vs panic 的选择
|
||
demonstrateErrorVsPanic()
|
||
}
|
||
|
||
// demonstratePanicBasics 演示 panic 的基本概念
|
||
func demonstratePanicBasics() {
|
||
fmt.Println("1. Panic 的基本概念:")
|
||
|
||
// panic 的基本特性
|
||
fmt.Printf(" Panic 的基本特性:\\n")
|
||
fmt.Printf(" - panic 是运行时错误,会导致程序崩溃\\n")
|
||
fmt.Printf(" - panic 会停止当前函数的执行\\n")
|
||
fmt.Printf(" - panic 会执行当前函数的 defer 语句\\n")
|
||
fmt.Printf(" - panic 会向上传播到调用栈\\n")
|
||
fmt.Printf(" - panic 可以被 recover 捕获\\n")
|
||
|
||
// 手动触发 panic
|
||
fmt.Printf(" 手动触发 panic:\\n")
|
||
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 捕获到 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
fmt.Printf(" 准备触发 panic\\n")
|
||
panic("这是一个手动触发的 panic")
|
||
fmt.Printf(" 这行代码不会执行\\n")
|
||
}()
|
||
|
||
// 常见的 panic 场景
|
||
fmt.Printf(" 常见的 panic 场景:\\n")
|
||
|
||
// 1. 数组越界
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 数组越界 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
arr := [3]int{1, 2, 3}
|
||
fmt.Printf(" 访问数组索引 5: %d\\n", arr[5]) // 会 panic
|
||
}()
|
||
|
||
// 2. 空指针解引用
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 空指针解引用 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
var ptr *int
|
||
fmt.Printf(" 解引用空指针: %d\\n", *ptr) // 会 panic
|
||
}()
|
||
|
||
// 3. 类型断言失败
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 类型断言失败 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
var x interface{} = "hello"
|
||
num := x.(int) // 会 panic
|
||
fmt.Printf(" 类型断言结果: %d\\n", num)
|
||
}()
|
||
|
||
// 4. 向已关闭的 channel 发送数据
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 向已关闭 channel 发送数据 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
ch := make(chan int)
|
||
close(ch)
|
||
ch <- 1 // 会 panic
|
||
}()
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateRecover 演示 recover 的使用
|
||
func demonstrateRecover() {
|
||
fmt.Println("2. Recover 的使用:")
|
||
|
||
// recover 的基本概念
|
||
fmt.Printf(" Recover 的基本概念:\\n")
|
||
fmt.Printf(" - recover 只能在 defer 函数中使用\\n")
|
||
fmt.Printf(" - recover 可以捕获当前 goroutine 的 panic\\n")
|
||
fmt.Printf(" - recover 返回 panic 的值\\n")
|
||
fmt.Printf(" - recover 后程序可以继续执行\\n")
|
||
|
||
// 基本 recover 示例
|
||
fmt.Printf(" 基本 recover 示例:\\n")
|
||
|
||
result := safeFunction(func() int {
|
||
panic("计算过程中发生错误")
|
||
return 42
|
||
})
|
||
fmt.Printf(" 安全函数返回: %d\\n", result)
|
||
|
||
// recover 的返回值
|
||
fmt.Printf(" recover 的返回值:\\n")
|
||
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" panic 值: %v\\n", r)
|
||
fmt.Printf(" panic 类型: %T\\n", r)
|
||
|
||
// 根据 panic 类型进行不同处理
|
||
switch v := r.(type) {
|
||
case string:
|
||
fmt.Printf(" 字符串 panic: %s\\n", v)
|
||
case int:
|
||
fmt.Printf(" 整数 panic: %d\\n", v)
|
||
case error:
|
||
fmt.Printf(" 错误 panic: %v\\n", v)
|
||
default:
|
||
fmt.Printf(" 其他类型 panic: %v\\n", v)
|
||
}
|
||
}
|
||
}()
|
||
|
||
// 触发不同类型的 panic
|
||
testPanics := []interface{}{
|
||
"字符串错误",
|
||
42,
|
||
fmt.Errorf("错误对象"),
|
||
struct{ Message string }{"结构体错误"},
|
||
}
|
||
|
||
for i, panicValue := range testPanics {
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 测试 %d - panic: %v (%T)\\n", i+1, r, r)
|
||
}
|
||
}()
|
||
panic(panicValue)
|
||
}()
|
||
}
|
||
}()
|
||
|
||
// recover 的条件
|
||
fmt.Printf(" recover 的条件:\\n")
|
||
|
||
// 正确的 recover 使用
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 正确捕获 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
panic("正确的 recover 示例")
|
||
}()
|
||
|
||
// 错误的 recover 使用
|
||
func() {
|
||
defer func() {
|
||
// 这个 recover 不会捕获到 panic,因为不是直接在 defer 中调用
|
||
go func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 错误的 recover: %v\\n", r)
|
||
}
|
||
}()
|
||
}()
|
||
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 正确捕获嵌套 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
panic("嵌套 panic 示例")
|
||
}()
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateDeferWithPanicRecover 演示 defer 与 panic/recover 的配合
|
||
func demonstrateDeferWithPanicRecover() {
|
||
fmt.Println("3. Defer 与 Panic/Recover 的配合:")
|
||
|
||
// defer 的执行顺序
|
||
fmt.Printf(" defer 的执行顺序:\\n")
|
||
|
||
func() {
|
||
defer fmt.Printf(" defer 1\\n")
|
||
defer fmt.Printf(" defer 2\\n")
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" recover: %v\\n", r)
|
||
}
|
||
}()
|
||
defer fmt.Printf(" defer 3\\n")
|
||
|
||
fmt.Printf(" 正常执行\\n")
|
||
panic("测试 defer 顺序")
|
||
fmt.Printf(" 这行不会执行\\n")
|
||
}()
|
||
|
||
// 多层 defer 和 recover
|
||
fmt.Printf(" 多层 defer 和 recover:\\n")
|
||
|
||
func() {
|
||
defer func() {
|
||
fmt.Printf(" 外层 defer 开始\\n")
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 外层 recover: %v\\n", r)
|
||
}
|
||
fmt.Printf(" 外层 defer 结束\\n")
|
||
}()
|
||
|
||
defer func() {
|
||
fmt.Printf(" 内层 defer 开始\\n")
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 内层 recover: %v\\n", r)
|
||
}
|
||
}()
|
||
fmt.Printf(" 内层 defer 结束\\n")
|
||
}()
|
||
|
||
panic("多层 defer 测试")
|
||
}()
|
||
|
||
// defer 中的资源清理
|
||
fmt.Printf(" defer 中的资源清理:\\n")
|
||
|
||
processWithCleanup := func() {
|
||
// 模拟资源分配
|
||
resource := "重要资源"
|
||
fmt.Printf(" 分配资源: %s\\n", resource)
|
||
|
||
defer func() {
|
||
// 资源清理
|
||
fmt.Printf(" 清理资源: %s\\n", resource)
|
||
|
||
// 捕获 panic
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 处理过程中发生 panic: %v\\n", r)
|
||
fmt.Printf(" 资源已安全清理\\n")
|
||
}
|
||
}()
|
||
|
||
// 模拟可能 panic 的操作
|
||
if time.Now().UnixNano()%2 == 0 {
|
||
panic("随机 panic")
|
||
}
|
||
|
||
fmt.Printf(" 正常处理完成\\n")
|
||
}
|
||
|
||
// 执行多次以演示不同情况
|
||
for i := 0; i < 3; i++ {
|
||
fmt.Printf(" 执行 %d:\\n", i+1)
|
||
processWithCleanup()
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstratePanicPropagation 演示 panic 的传播
|
||
func demonstratePanicPropagation() {
|
||
fmt.Println("4. Panic 的传播:")
|
||
|
||
// panic 的栈展开
|
||
fmt.Printf(" panic 的栈展开:\\n")
|
||
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 最终捕获 panic: %v\\n", r)
|
||
|
||
// 打印调用栈
|
||
buf := make([]byte, 1024)
|
||
n := runtime.Stack(buf, false)
|
||
fmt.Printf(" 调用栈:\\n%s\\n", buf[:n])
|
||
}
|
||
}()
|
||
|
||
level1()
|
||
}()
|
||
|
||
// panic 的中途拦截
|
||
fmt.Printf(" panic 的中途拦截:\\n")
|
||
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 最外层捕获: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
interceptedLevel1()
|
||
}()
|
||
|
||
// panic 的重新抛出
|
||
fmt.Printf(" panic 的重新抛出:\\n")
|
||
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 最终处理 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
rethrowLevel1()
|
||
}()
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstratePracticalUsage 演示 panic/recover 的实际应用
|
||
func demonstratePracticalUsage() {
|
||
fmt.Println("5. Panic/Recover 的实际应用:")
|
||
|
||
// Web 服务器中的 panic 恢复
|
||
fmt.Printf(" Web 服务器中的 panic 恢复:\\n")
|
||
|
||
requests := []string{
|
||
"/api/users",
|
||
"/api/panic",
|
||
"/api/orders",
|
||
"/api/error",
|
||
}
|
||
|
||
for _, path := range requests {
|
||
handleHTTPRequest(path)
|
||
}
|
||
|
||
// 工作池中的 panic 处理
|
||
fmt.Printf(" 工作池中的 panic 处理:\\n")
|
||
|
||
jobs := []Job{
|
||
{ID: 1, Data: "正常任务"},
|
||
{ID: 2, Data: "panic任务"},
|
||
{ID: 3, Data: "正常任务"},
|
||
{ID: 4, Data: "error任务"},
|
||
}
|
||
|
||
for _, job := range jobs {
|
||
processJobSafely(job)
|
||
}
|
||
|
||
// 数据解析中的 panic 处理
|
||
fmt.Printf(" 数据解析中的 panic 处理:\\n")
|
||
|
||
data := []string{
|
||
"123",
|
||
"abc",
|
||
"456",
|
||
"",
|
||
"789",
|
||
}
|
||
|
||
for _, item := range data {
|
||
result := parseIntSafely(item)
|
||
fmt.Printf(" 解析 '%s': %d\\n", item, result)
|
||
}
|
||
|
||
// 递归函数中的 panic 处理
|
||
fmt.Printf(" 递归函数中的 panic 处理:\\n")
|
||
|
||
testValues := []int{5, -1, 10, 0}
|
||
for _, n := range testValues {
|
||
result := safeFactorial(n)
|
||
fmt.Printf(" %d! = %d\\n", n, result)
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateBestPractices 演示最佳实践
|
||
func demonstrateBestPractices() {
|
||
fmt.Println("6. 最佳实践:")
|
||
|
||
// 最佳实践原则
|
||
fmt.Printf(" 最佳实践原则:\\n")
|
||
fmt.Printf(" 1. 优先使用错误返回值而不是 panic\\n")
|
||
fmt.Printf(" 2. 只在真正异常的情况下使用 panic\\n")
|
||
fmt.Printf(" 3. 在库代码中避免 panic,转换为错误\\n")
|
||
fmt.Printf(" 4. 在应用边界使用 recover 保护\\n")
|
||
fmt.Printf(" 5. 记录 panic 信息用于调试\\n")
|
||
|
||
// 库函数的 panic 处理
|
||
fmt.Printf(" 库函数的 panic 处理:\\n")
|
||
|
||
// 不好的做法:库函数直接 panic
|
||
fmt.Printf(" 不好的做法示例:\\n")
|
||
result, err := callLibraryFunction("invalid")
|
||
if err != nil {
|
||
fmt.Printf(" 库函数错误: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" 库函数结果: %s\\n", result)
|
||
}
|
||
|
||
// 好的做法:将 panic 转换为错误
|
||
fmt.Printf(" 好的做法示例:\\n")
|
||
result, err = safeLibraryFunction("invalid")
|
||
if err != nil {
|
||
fmt.Printf(" 安全库函数错误: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" 安全库函数结果: %s\\n", result)
|
||
}
|
||
|
||
// panic 信息的记录
|
||
fmt.Printf(" panic 信息的记录:\\n")
|
||
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
logPanic(r)
|
||
}
|
||
}()
|
||
|
||
panic("需要记录的 panic")
|
||
}()
|
||
|
||
// 优雅的服务关闭
|
||
fmt.Printf(" 优雅的服务关闭:\\n")
|
||
|
||
server := &Server{name: "测试服务器"}
|
||
server.Start()
|
||
|
||
// 模拟服务运行中的 panic
|
||
func() {
|
||
defer server.handlePanic()
|
||
panic("服务运行时错误")
|
||
}()
|
||
|
||
server.Stop()
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// demonstrateErrorVsPanic 演示错误处理 vs panic 的选择
|
||
func demonstrateErrorVsPanic() {
|
||
fmt.Println("7. 错误处理 vs Panic 的选择:")
|
||
|
||
// 使用错误的场景
|
||
fmt.Printf(" 使用错误的场景:\\n")
|
||
fmt.Printf(" - 预期可能发生的错误\\n")
|
||
fmt.Printf(" - 用户输入错误\\n")
|
||
fmt.Printf(" - 网络或I/O错误\\n")
|
||
fmt.Printf(" - 业务逻辑错误\\n")
|
||
|
||
// 使用 panic 的场景
|
||
fmt.Printf(" 使用 panic 的场景:\\n")
|
||
fmt.Printf(" - 程序逻辑错误\\n")
|
||
fmt.Printf(" - 不可恢复的错误\\n")
|
||
fmt.Printf(" - 初始化失败\\n")
|
||
fmt.Printf(" - 断言失败\\n")
|
||
|
||
// 对比示例
|
||
fmt.Printf(" 对比示例:\\n")
|
||
|
||
// 错误处理示例
|
||
fmt.Printf(" 错误处理示例:\\n")
|
||
result, err := divide(10, 0)
|
||
if err != nil {
|
||
fmt.Printf(" 除法错误: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" 除法结果: %.2f\\n", result)
|
||
}
|
||
|
||
// panic 示例
|
||
fmt.Printf(" panic 示例:\\n")
|
||
func() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 捕获 panic: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
assertPositive(-5)
|
||
}()
|
||
|
||
// 混合使用示例
|
||
fmt.Printf(" 混合使用示例:\\n")
|
||
|
||
calculator := &Calculator{}
|
||
|
||
// 正常计算
|
||
result, err = calculator.Calculate("10 + 5")
|
||
if err != nil {
|
||
fmt.Printf(" 计算错误: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" 计算结果: %.2f\\n", result)
|
||
}
|
||
|
||
// 无效表达式
|
||
result, err = calculator.Calculate("invalid")
|
||
if err != nil {
|
||
fmt.Printf(" 计算错误: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" 计算结果: %.2f\\n", result)
|
||
}
|
||
|
||
// 内部 panic 被转换为错误
|
||
result, err = calculator.Calculate("panic")
|
||
if err != nil {
|
||
fmt.Printf(" 计算错误: %v\\n", err)
|
||
} else {
|
||
fmt.Printf(" 计算结果: %.2f\\n", result)
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// ========== 辅助函数和类型定义 ==========
|
||
|
||
// safeFunction 安全执行函数
|
||
func safeFunction(fn func() int) int {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 函数执行中发生 panic,已恢复: %v\\n", r)
|
||
}
|
||
}()
|
||
|
||
return fn()
|
||
}
|
||
|
||
// level1 第一层函数
|
||
func level1() {
|
||
defer func() {
|
||
fmt.Printf(" level1 defer 执行\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 level1\\n")
|
||
level2()
|
||
fmt.Printf(" level1 正常结束\\n")
|
||
}
|
||
|
||
// level2 第二层函数
|
||
func level2() {
|
||
defer func() {
|
||
fmt.Printf(" level2 defer 执行\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 level2\\n")
|
||
level3()
|
||
fmt.Printf(" level2 正常结束\\n")
|
||
}
|
||
|
||
// level3 第三层函数
|
||
func level3() {
|
||
defer func() {
|
||
fmt.Printf(" level3 defer 执行\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 level3\\n")
|
||
panic("level3 中的 panic")
|
||
fmt.Printf(" level3 正常结束\\n")
|
||
}
|
||
|
||
// interceptedLevel1 中途拦截的第一层
|
||
func interceptedLevel1() {
|
||
defer func() {
|
||
fmt.Printf(" interceptedLevel1 defer\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 interceptedLevel1\\n")
|
||
interceptedLevel2()
|
||
fmt.Printf(" interceptedLevel1 正常结束\\n")
|
||
}
|
||
|
||
// interceptedLevel2 中途拦截的第二层
|
||
func interceptedLevel2() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" interceptedLevel2 拦截 panic: %v\\n", r)
|
||
// 不重新抛出,panic 在这里被处理
|
||
}
|
||
fmt.Printf(" interceptedLevel2 defer\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 interceptedLevel2\\n")
|
||
interceptedLevel3()
|
||
fmt.Printf(" interceptedLevel2 正常结束\\n")
|
||
}
|
||
|
||
// interceptedLevel3 中途拦截的第三层
|
||
func interceptedLevel3() {
|
||
defer func() {
|
||
fmt.Printf(" interceptedLevel3 defer\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 interceptedLevel3\\n")
|
||
panic("interceptedLevel3 中的 panic")
|
||
fmt.Printf(" interceptedLevel3 正常结束\\n")
|
||
}
|
||
|
||
// rethrowLevel1 重新抛出的第一层
|
||
func rethrowLevel1() {
|
||
defer func() {
|
||
fmt.Printf(" rethrowLevel1 defer\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 rethrowLevel1\\n")
|
||
rethrowLevel2()
|
||
fmt.Printf(" rethrowLevel1 正常结束\\n")
|
||
}
|
||
|
||
// rethrowLevel2 重新抛出的第二层
|
||
func rethrowLevel2() {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" rethrowLevel2 捕获并重新抛出 panic: %v\\n", r)
|
||
panic(fmt.Sprintf("重新抛出: %v", r)) // 重新抛出
|
||
}
|
||
fmt.Printf(" rethrowLevel2 defer\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 rethrowLevel2\\n")
|
||
rethrowLevel3()
|
||
fmt.Printf(" rethrowLevel2 正常结束\\n")
|
||
}
|
||
|
||
// rethrowLevel3 重新抛出的第三层
|
||
func rethrowLevel3() {
|
||
defer func() {
|
||
fmt.Printf(" rethrowLevel3 defer\\n")
|
||
}()
|
||
|
||
fmt.Printf(" 进入 rethrowLevel3\\n")
|
||
panic("rethrowLevel3 中的 panic")
|
||
fmt.Printf(" rethrowLevel3 正常结束\\n")
|
||
}
|
||
|
||
// handleHTTPRequest 处理HTTP请求
|
||
func handleHTTPRequest(path string) {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" HTTP请求 %s 发生 panic,已恢复: %v\\n", path, r)
|
||
// 在实际应用中,这里会返回 500 错误
|
||
}
|
||
}()
|
||
|
||
fmt.Printf(" 处理请求: %s\\n", path)
|
||
|
||
switch path {
|
||
case "/api/panic":
|
||
panic("模拟处理器 panic")
|
||
case "/api/error":
|
||
// 这里应该返回错误而不是 panic
|
||
fmt.Printf(" 请求处理出错\\n")
|
||
default:
|
||
fmt.Printf(" 请求处理成功\\n")
|
||
}
|
||
}
|
||
|
||
// Job 任务结构
|
||
type Job struct {
|
||
ID int
|
||
Data string
|
||
}
|
||
|
||
// processJobSafely 安全处理任务
|
||
func processJobSafely(job Job) {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 任务 %d 处理时发生 panic,已恢复: %v\\n", job.ID, r)
|
||
}
|
||
}()
|
||
|
||
fmt.Printf(" 处理任务 %d: %s\\n", job.ID, job.Data)
|
||
|
||
switch job.Data {
|
||
case "panic任务":
|
||
panic("任务处理中的 panic")
|
||
case "error任务":
|
||
fmt.Printf(" 任务处理出错\\n")
|
||
default:
|
||
fmt.Printf(" 任务处理成功\\n")
|
||
}
|
||
}
|
||
|
||
// parseIntSafely 安全解析整数
|
||
func parseIntSafely(s string) int {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 解析 '%s' 时发生 panic: %v\\n", s, r)
|
||
}
|
||
}()
|
||
|
||
if s == "" {
|
||
panic("空字符串无法解析")
|
||
}
|
||
|
||
result, err := strconv.Atoi(s)
|
||
if err != nil {
|
||
return 0 // 返回默认值
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// safeFactorial 安全计算阶乘
|
||
func safeFactorial(n int) int {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 计算 %d! 时发生 panic: %v\\n", n, r)
|
||
}
|
||
}()
|
||
|
||
return factorial(n)
|
||
}
|
||
|
||
// factorial 计算阶乘(可能 panic)
|
||
func factorial(n int) int {
|
||
if n < 0 {
|
||
panic("负数没有阶乘")
|
||
}
|
||
if n == 0 || n == 1 {
|
||
return 1
|
||
}
|
||
return n * factorial(n-1)
|
||
}
|
||
|
||
// callLibraryFunction 库函数(不好的做法)
|
||
func callLibraryFunction(input string) (string, error) {
|
||
// 这个函数直接 panic,不是好的做法
|
||
if input == "invalid" {
|
||
panic("库函数中的 panic")
|
||
}
|
||
return "处理结果: " + input, nil
|
||
}
|
||
|
||
// safeLibraryFunction 安全的库函数
|
||
func safeLibraryFunction(input string) (result string, err error) {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
err = fmt.Errorf("库函数内部错误: %v", r)
|
||
}
|
||
}()
|
||
|
||
// 调用可能 panic 的函数
|
||
return callLibraryFunction(input)
|
||
}
|
||
|
||
// logPanic 记录 panic 信息
|
||
func logPanic(r interface{}) {
|
||
fmt.Printf(" [PANIC LOG] 时间: %s\\n", time.Now().Format("2006-01-02 15:04:05"))
|
||
fmt.Printf(" [PANIC LOG] 错误: %v\\n", r)
|
||
fmt.Printf(" [PANIC LOG] 类型: %T\\n", r)
|
||
|
||
// 获取调用栈
|
||
buf := make([]byte, 1024)
|
||
n := runtime.Stack(buf, false)
|
||
fmt.Printf(" [PANIC LOG] 调用栈:\\n%s\\n", buf[:n])
|
||
}
|
||
|
||
// Server 服务器结构
|
||
type Server struct {
|
||
name string
|
||
}
|
||
|
||
func (s *Server) Start() {
|
||
fmt.Printf(" 服务器 %s 启动\\n", s.name)
|
||
}
|
||
|
||
func (s *Server) Stop() {
|
||
fmt.Printf(" 服务器 %s 停止\\n", s.name)
|
||
}
|
||
|
||
func (s *Server) handlePanic() {
|
||
if r := recover(); r != nil {
|
||
fmt.Printf(" 服务器 %s 捕获 panic: %v\\n", s.name, r)
|
||
fmt.Printf(" 服务器继续运行\\n")
|
||
}
|
||
}
|
||
|
||
// divide 除法运算
|
||
func divide(a, b float64) (float64, error) {
|
||
if b == 0 {
|
||
return 0, fmt.Errorf("除数不能为零")
|
||
}
|
||
return a / b, nil
|
||
}
|
||
|
||
// assertPositive 断言为正数
|
||
func assertPositive(n int) {
|
||
if n <= 0 {
|
||
panic(fmt.Sprintf("断言失败: %d 不是正数", n))
|
||
}
|
||
fmt.Printf(" 断言通过: %d 是正数\\n", n)
|
||
}
|
||
|
||
// Calculator 计算器
|
||
type Calculator struct{}
|
||
|
||
func (c *Calculator) Calculate(expression string) (result float64, err error) {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
err = fmt.Errorf("计算表达式 '%s' 时发生内部错误: %v", expression, r)
|
||
}
|
||
}()
|
||
|
||
switch expression {
|
||
case "10 + 5":
|
||
return 15, nil
|
||
case "invalid":
|
||
return 0, fmt.Errorf("无效的表达式: %s", expression)
|
||
case "panic":
|
||
panic("计算器内部 panic")
|
||
default:
|
||
return 0, fmt.Errorf("不支持的表达式: %s", expression)
|
||
}
|
||
}
|
||
|
||
/*
|
||
运行这个程序:
|
||
go run 03-panic-recover.go
|
||
|
||
学习要点:
|
||
1. panic 是运行时错误,会导致程序崩溃
|
||
2. recover 只能在 defer 函数中使用来捕获 panic
|
||
3. defer 语句按照 LIFO 顺序执行
|
||
4. panic 会沿着调用栈向上传播
|
||
5. panic/recover 应该谨慎使用,优先使用错误处理
|
||
|
||
Panic 的特性:
|
||
1. 停止当前函数的正常执行
|
||
2. 执行当前函数的所有 defer 语句
|
||
3. 向上传播到调用函数
|
||
4. 如果没有被 recover,程序会崩溃
|
||
5. 可以携带任意类型的值
|
||
|
||
Recover 的特性:
|
||
1. 只能在 defer 函数中直接调用
|
||
2. 返回 panic 的值,如果没有 panic 返回 nil
|
||
3. 只能捕获当前 goroutine 的 panic
|
||
4. 捕获后程序可以继续执行
|
||
5. 可以根据 panic 值进行不同处理
|
||
|
||
常见的 Panic 场景:
|
||
1. 数组或切片越界访问
|
||
2. 空指针解引用
|
||
3. 类型断言失败(不安全断言)
|
||
4. 向已关闭的 channel 发送数据
|
||
5. 除零操作(整数除法)
|
||
|
||
使用场景:
|
||
1. Panic 适用场景:
|
||
- 程序逻辑错误
|
||
- 不可恢复的错误
|
||
- 初始化失败
|
||
- 断言失败
|
||
|
||
2. Error 适用场景:
|
||
- 预期可能发生的错误
|
||
- 用户输入错误
|
||
- 网络或I/O错误
|
||
- 业务逻辑错误
|
||
|
||
最佳实践:
|
||
1. 优先使用错误返回值而不是 panic
|
||
2. 只在真正异常的情况下使用 panic
|
||
3. 在库代码中避免 panic,转换为错误
|
||
4. 在应用边界使用 recover 保护
|
||
5. 记录 panic 信息用于调试
|
||
6. 使用 defer 进行资源清理
|
||
|
||
注意事项:
|
||
1. recover 只能在 defer 中直接调用
|
||
2. 不要忽略 recover 的返回值
|
||
3. panic 会影响程序性能
|
||
4. 过度使用 panic/recover 会使代码难以理解
|
||
5. 在 goroutine 中的 panic 需要单独处理
|
||
|
||
实际应用:
|
||
1. Web 服务器的 panic 恢复中间件
|
||
2. 工作池中的任务 panic 处理
|
||
3. 数据解析中的异常处理
|
||
4. 递归函数的栈溢出保护
|
||
5. 库函数的 panic 转错误
|
||
|
||
性能考虑:
|
||
1. panic/recover 有一定的性能开销
|
||
2. 不应该用于正常的控制流
|
||
3. 频繁的 panic/recover 会影响性能
|
||
4. 错误处理通常比 panic/recover 更高效
|
||
5. 在性能敏感的代码中谨慎使用
|
||
*/
|