panic与recover #

一、panic概述 #

panic会停止当前函数执行,开始展开goroutine的栈。

1.1 触发panic #

go
panic("something went wrong")

1.2 panic传播 #

go
func main() {
    defer fmt.Println("defer in main")
    
    func() {
        defer fmt.Println("defer in anonymous")
        panic("panic!")
    }()
}

输出:

text
defer in anonymous
defer in main
panic: panic!

二、recover概述 #

recover可以捕获panic,阻止程序崩溃。

2.1 基本用法 #

go
func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered:", r)
        }
    }()
    
    panic("panic!")
    fmt.Println("Never reached")
}

2.2 只在defer中有效 #

go
func main() {
    r := recover()  // 无效,不在defer中
    panic("panic!")
}

三、使用场景 #

3.1 防止程序崩溃 #

go
func safeOperation() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v", r)
        }
    }()
    
    riskyOperation()
    return nil
}

3.2 HTTP服务器 #

go
func handler(w http.ResponseWriter, r *http.Request) {
    defer func() {
        if err := recover(); err != nil {
            http.Error(w, "Internal Server Error", 500)
            log.Printf("panic: %v", err)
        }
    }()
    
    processRequest(r)
}

3.3 goroutine恢复 #

go
func safeGo(fn func()) {
    go func() {
        defer func() {
            if r := recover(); r != nil {
                log.Printf("goroutine panic: %v", r)
            }
        }()
        fn()
    }()
}

四、实际应用 #

4.1 通用恢复函数 #

go
func RecoverMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("panic: %v\n%s", err, debug.Stack())
                http.Error(w, "Internal Server Error", 500)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

4.2 任务执行器 #

go
type Executor struct {
    tasks []func()
}

func (e *Executor) Run() {
    for _, task := range e.tasks {
        func() {
            defer func() {
                if r := recover(); r != nil {
                    log.Printf("Task panic: %v", r)
                }
            }()
            task()
        }()
    }
}

4.3 插件系统 #

go
func runPlugin(p Plugin) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("plugin panic: %v", r)
        }
    }()
    
    return p.Execute()
}

五、panic vs error #

5.1 使用error的场景 #

  • 可预期的错误
  • 函数返回错误
  • 需要处理的错误
go
func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

5.2 使用panic的场景 #

  • 不可恢复的错误
  • 程序员错误
  • 初始化失败
go
func MustCompile(pattern string) *regexp.Regexp {
    re, err := regexp.Compile(pattern)
    if err != nil {
        panic(err)
    }
    return re
}

六、最佳实践 #

6.1 优先使用error #

go
// 好
func process() error {
    if err := validate(); err != nil {
        return err
    }
    return nil
}

// 避免
func process() {
    if err := validate(); err != nil {
        panic(err)
    }
}

6.2 recover在defer中 #

go
defer func() {
    if r := recover(); r != nil {
        // 处理panic
    }
}()

6.3 记录堆栈 #

go
import "runtime/debug"

defer func() {
    if r := recover(); r != nil {
        log.Printf("panic: %v\n%s", r, debug.Stack())
    }
}()

七、常见错误 #

7.1 滥用panic #

go
// 不好:用panic处理正常错误
func validate(input string) {
    if input == "" {
        panic("empty input")
    }
}

// 好:返回error
func validate(input string) error {
    if input == "" {
        return errors.New("empty input")
    }
    return nil
}

7.2 忘记recover #

go
func main() {
    go func() {
        panic("panic!")  // 整个程序崩溃
    }()
}

7.3 recover位置错误 #

go
func main() {
    recover()  // 无效
    
    defer func() {
        recover()  // 有效
    }()
    
    panic("panic!")
}

八、总结 #

panic与recover要点:

特性 说明
panic 触发异常
recover 捕获异常
defer recover必须在defer中

关键点:

  1. panic触发:停止执行,展开栈
  2. recover捕获:阻止程序崩溃
  3. defer中使用:recover只在defer中有效
  4. 优先error:正常错误使用error
  5. 记录堆栈:使用debug.Stack()

准备好学习包管理了吗?让我们进入下一章!

最后更新:2026-03-26