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中 |
关键点:
- panic触发:停止执行,展开栈
- recover捕获:阻止程序崩溃
- defer中使用:recover只在defer中有效
- 优先error:正常错误使用error
- 记录堆栈:使用debug.Stack()
准备好学习包管理了吗?让我们进入下一章!
最后更新:2026-03-26