Golang中defer与recover的组合使用示例代码

 更新时间:2025年06月10日 11:02:12   作者:星星点点洲  
Go语言的defer和recover机制为开发者提供了一种优雅处理错误的方式,帮助保持程序的稳定性和可维护性,这篇文章主要介绍了Golang中defer与recover组合使用的相关资料,需要的朋友可以参考下

recover运行的条件:

  • 该协程必须出现了panic
  • recover函数必须在和panic同级的defer中被调用

在Go语言中,deferrecover是两个关键特性,通常结合使用以处理资源管理和异常恢复。以下是它们的核心应用场景及使用示例:

1. defer 的应用场景

defer用于延迟执行函数调用,确保在函数退出前执行特定操作。主要用途包括:

资源释放

  • 文件操作:确保文件句柄关闭。

    func readFile(filename string) error {
        file, err := os.Open(filename)
        if err != nil {
            return err
        }
        defer file.Close() // 确保函数返回前关闭文件
        // 处理文件内容...
        return nil
    }
    
  • 锁释放:防止死锁。

    var mu sync.Mutex
    func updateData() {
        mu.Lock()
        defer mu.Unlock() // 函数退出时自动释放锁
        // 修改共享数据...
    }
    

事务回滚

  • 数据库或业务逻辑中,确保操作失败时回滚。
    func transferMoney() {
        tx := db.Begin()
        defer func() {
            if r := recover(); r != nil { // 结合recover处理panic
                tx.Rollback()
            }
        }()
        // 执行转账操作,可能触发panic
        tx.Commit()
    }
    

2. recover 的应用场景

recover用于捕获panic,防止程序非正常终止。必须在defer函数中调用

全局异常恢复

  • 防止因未处理的panic导致程序崩溃。
    func safeCall() {
        defer func() {
            if r := recover(); r != nil {
                fmt.Println("Recovered from panic:", r)
            }
        }()
        // 可能触发panic的代码
        panic("unexpected error")
    }
    

保护Goroutine

  • 避免某个Goroutine的panic影响整个程序。
    func startWorker() {
        go func() {
            defer func() {
                if r := recover(); r != nil {
                    log.Println("Worker panic:", r)
                }
            }()
            // Goroutine的业务逻辑...
        }()
    }
    

优雅降级

  • panic转换为错误,保持服务可用性。
    func safeHandler() (err error) {
        defer func() {
            if r := recover(); r != nil {
                err = fmt.Errorf("internal error: %v", r)
            }
        }()
        // 可能panic的代码
        return nil
    }
    

3. 结合使用示例

func processRequest() (err error) {
    // 恢复panic并转为错误
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic occurred: %v", r)
        }
    }()

    // 资源管理示例
    file, err := os.Open("data.txt")
    if err != nil {
        return err
    }
    defer file.Close() // 确保文件关闭

    // 业务逻辑(可能触发panic)
    if someCondition {
        panic("data corruption")
    }

    return nil
}

4. 注意事项

  • recover仅在defer中有效:非defer上下文中调用会返回nil
  • 避免滥用recover:隐藏panic可能导致未知状态,应仅在必要时使用。
  • 明确错误处理:优先返回错误而非依赖panic/recover,后者适用于不可恢复的异常(如程序逻辑错误)。

通过合理使用deferrecover,可以显著提升Go程序的健壮性和可维护性,尤其在资源管理和异常恢复场景中。

在Go语言中,上述代码无法正确捕获panic,原因如下:

5. 问题分析

代码示例

func safeCall() {
    // 直接调用recover(不在defer中)
    if r := recover(); r != nil {
        fmt.Println("Recovered from panic:", r)
    }
    // 触发新的panic
    panic("unexpected error")
}

// 将safeCall包裹在defer中
defer func() { safeCall() }()

关键问题

  • recover未在defer中调用

    • safeCall中的recover直接调用,而非通过defer注册的函数。此时recover会在safeCall正常执行时立即触发,而非在panic发生后被动调用。
    • 若此时未发生panicrecover返回nil,无法捕获后续触发的panic
  • panic与defer执行顺序

    • 当外层函数触发panic时,会先执行已注册的defer函数。
    • defer中调用safeCall,此时safeCall内的recover尝试捕获当前panic,但随后safeCall自身又触发了一个新的panic("unexpected error"),而新的panic未被任何recover处理,导致程序崩溃。

6. 正确写法

修复方案

recover放在defer函数中,并直接与可能触发panic的代码关联:

func safeCall() {
    // 可能触发panic的代码
    defer func() {
        // 在defer中调用recover
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    panic("unexpected error")
}

// 注册defer
defer safeCall()

执行逻辑

  • 调用defer safeCall(),注册safeCall到外层函数的defer栈。
  • 当外层函数触发panic时,执行safeCall
  • safeCall内部的defer函数中的recover会捕获当前panic阻止其继续传播
  • safeCall自身触发panic,该panic会被其自身的defer recover捕获。

7. 错误示例的详细解释

原代码执行流程

假设外层函数触发panic

  • 外层函数执行panic("outer panic")
  • 程序开始处理defer,调用defer func() { safeCall() }()
  • safeCall执行:
    • recover()尝试捕获外层panic("outer panic"),打印恢复信息。
    • 随后触发新的panic("unexpected error")
  • 新的panic未被任何recover处理,导致程序崩溃。

关键结论

  • recover必须通过defer注册的函数被动调用,才能捕获到panic
  • 若在普通代码中直接调用recover,只有在已发生panic且未被处理时才会生效。

总结

  • 必须将recover放在defer函数中,才能确保在panic发生后被动调用。
  • 避免在恢复逻辑中触发新的panic,否则需要额外的recover处理。
  • 正确的deferrecover组合是资源管理和异常恢复的核心模式。

通过调整代码结构,确保recoverdefer中调用,即可正确捕获并处理panic

到此这篇关于Golang中defer与recover组合使用的文章就介绍到这了,更多相关Go defer与recover组合使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • GO语言实现的端口扫描器分享

    GO语言实现的端口扫描器分享

    这篇文章主要介绍了GO语言实现的端口扫描器分享,本文直接给出实现代码,代码中包含大量注释,需要的朋友可以参考下
    2014-10-10
  • Go语音开发中常见Error类型处理示例详解

    Go语音开发中常见Error类型处理示例详解

    这篇文章主要为大家介绍了Go语音开发中常见Error类型处理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Golang使用Redis与连接池方式

    Golang使用Redis与连接池方式

    这篇文章主要介绍了Golang使用Redis与连接池方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • goland 实现websocket server的示例代码

    goland 实现websocket server的示例代码

    本文主要介绍了goland 实现websocket server的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • 使用go语言解析xml的实现方法(必看篇)

    使用go语言解析xml的实现方法(必看篇)

    下面小编就为大家带来一篇使用go语言解析xml的实现方法(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • go 压缩解压zip文件源码示例

    go 压缩解压zip文件源码示例

    这篇文章主要为大家介绍了go压缩及解压zip文件的源码示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Go语言中的变量声明和赋值

    Go语言中的变量声明和赋值

    这篇文章主要介绍了Go语言中的变量声明和赋值的方法,十分的细致全面,有需要的小伙伴可以参考下。
    2015-04-04
  • GO 使用Webhook 实现github 自动化部署的方法

    GO 使用Webhook 实现github 自动化部署的方法

    这篇文章主要介绍了GO 使用Webhook 实现github 自动化部署的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • golang 自旋锁的实现

    golang 自旋锁的实现

    这篇文章主要介绍了golang 自旋锁的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • Go语言map不支持并发写操作的原因

    Go语言map不支持并发写操作的原因

    Go语言为什么不支持并发读写map​,Go官方的说法是在多数情况下map只存在并发读操作,如果原生支持并发读写,即降低了并发读操作的性能,在使用 map 时,要特别注意是否存在对 map 的并发写操作,如果存在,要结合 sync 包的互斥锁一起使用,
    2024-01-01

最新评论