golang之资源释放/异常错误处理解析

 更新时间:2024年01月07日 09:00:22   作者:后厂村村长  
这篇文章主要为大家介绍了golang之资源释放/异常错误处理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

资源释放-defer

类似于其他语言的 析构函数 比如PHP的 __destruct

func b() error {
    resp, err := http.Get("xxx.com")
    // 先判断操作是否成功
    if err != nil {
        return err
    }
    // 如果操作成功,再进行Close操作
    defer resp.Body.Close()
    return nil
}

从上面的示例可看到,defer 是个延迟函数(延迟执行)

关于 defer 的一些常见误区和陷阱

defer 语句是用来延迟执行函数的,且延迟发生在函数 return 之后具体指: Go return 在底层等于: 返回值入栈, defer函数调用, return

func returnValues() int {
    var result int
    defer func() {
        // defer 匿名函数对变量的修改发生在 return 之后
        result++
        fmt.Println("defer")
    }()
    return result
}
// 如下输出 0
fmt.Println(returnValues())

go允许多个defer同时存在

defer 执行顺序遵循先进后出 first-in-last-out (FILO)属于栈操作,如下输出:3、2、1

func stackingDefers() { 
    defer func() {
        fmt.Println("1")
    }()
    defer func() {
        fmt.Println("2")
    }()
    defer func() {
        fmt.Println("3")
    }()
}

函数被 deferred 时涉及的 参数变量值 在 defer 函数编写时确定,而非实际调用时

func c() {
    i := 0
    defer fmt.Println(i) // 此时i变量值已被确定,即0
    i++
    defer fmt.Println(i) // 此时i++已执行,变量值已被确定为 1
}
// 输出 1 0;因为defer的执行顺序为 后进先出,所以先输出 i++ 之后的 1;再输出之前的 0

defer与闭包陷阱

先来看一个示例:

func main() {
    i := 1
    defer func() {
        fmt.Println("defer-闭包i值:", i)
    }()
    defer fmt.Println("defer-i值:", i)
    i += 100
}

结果输出为:

defer-i值: 1
defer-闭包i值: 101

为啥捏?

  • 第二个defer,因为函数被 deferred 时涉及的 参数变量值 在 defer 函数编写时已确定,所以输出 defer-i值: 1
  • 第一个defer,闭包中的参数传递是引用类型传递,因此匿名函数中的 i 可以输出 101(匿名函数都是闭包,按引用类型传递)

try-catch-finally

Go追求简洁优雅,所以不支持传统的 try-catch-finally 结构,因为Go语言的设计者们认为,将异常与控制结构混在一起很容易使得代码变混乱(咱不懂,但内心大受震撼)。

如果要在go实现这么个逻辑,需要用到这么几个关键词:defer, panic, recover如何使用 defer 执行 try-catch-finally 调试:

func main() {
    // 类似其他语言的 try (隐式包含)
    defer func() {
        // 捕获异常:类似 catch(\Exception $e)
        if err := recover(); err != nil {
            // 输出异常
            fmt.Println(err)
            // 打印错误堆栈: 类似于PHP的 debug_print_backtrace
            debug.PrintStack() // 输出到日志即可
            fmt.Println()
            fmt.Println("执行:planB!")
            planB()
        }
    }()
    // 注意!!用recover处理panic指令,defer 须在 panic 之前声明,否则panic时会直接退出,recover无法捕获到panic
    tryCatchFinally()
    fmt.Println("dadada...") // 这里不会执行
}
func tryCatchFinally() {
    fmt.Println("begin")
    panic("抛出异常信息: throw new exception")
    fmt.Println("end") // 这里不会执行
}
func planB() {
    fmt.Println("exec-planB, done!!")
}

关于内建函数 recover

1、用来控制一个 goroutine 的 panicking 行为,捕获panic,从而影响应用的行为
2、一般调用方式

  • 在defer函数中,通过 recover 来终止一个 gojroutine 的 panicking 过程,从而恢复正常逻辑的执行
  • 获取通过panic传递的error,执行捕获之后的逻辑,参考传统tryCatchFinally

EOF - 错误处理

另一种常见错误处理,即 EOF(End-Of-File)文件结尾错误,可以直接捕获,示例如下:

reader := strings.NewReader("clear is better than clever")
p := make([]byte, 5)
for {
    n, err := reader.Read(p)
    if err != nil {
        if err == io.EOF {
            fmt.Println("EOF:", n)
        } else {
            fmt.Println(err)
        }
        os.Exit(1)
    }
    // 注: p 每次循环都会被覆盖,但如果最后一行不足5个字节时:
    // 仅会覆盖最后一行获取到的相应的字节数,比如最后一行3字节,仅覆盖p的前3个,第4、5个字节维持上次循环时的赋值
    // 所以这里输出时使用了:p[:n]
    fmt.Println(n, string(p[:n]))
}

如上执行结果:

5 clear
5  is b
5 etter
5  than
5  clev
2 erlev
EOF: 0
exit status 1

以上就是golang之资源释放/异常错误处理解析的详细内容,更多关于golang资源释放异常错误处理的资料请关注脚本之家其它相关文章!

相关文章

  • 浅析Go 字符串指纹

    浅析Go 字符串指纹

    这篇文章主要介绍了Go 字符串指纹的相关资料,帮助大家更好的理解和学习go语言,感兴趣的朋友可以了解下
    2020-09-09
  • 获取Golang环境变量的三种方式小结

    获取Golang环境变量的三种方式小结

    本文介绍了Golang中获取环境变量的三种方式,包含使用Viper包、GoDotEnv包和os包,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-11-11
  • Golang使用gin框架实现一个完整的聊天室功能

    Golang使用gin框架实现一个完整的聊天室功能

    由于我们项目的需要,我就研究了一下关于websocket的相关内容,去实现一个聊天室的功能,经过几天的探索,现在使用Gin框架实现了一个完整的聊天室+消息实时通知系统,感兴趣的小伙伴欢迎阅读本文
    2023-08-08
  • golang通过node_exporter监控GPU及cpu频率、温度的代码

    golang通过node_exporter监控GPU及cpu频率、温度的代码

    node_exporter这个开源组件是配合prometheus收集主机操作系统层的metrics的常用组件,但是官方没有提供GPU卡的metrics的采集,今天通过本文给大家介绍golang通过node_exporter监控GPU及cpu频率、温度的相关知识,感兴趣的朋友一起看看吧
    2022-05-05
  • Golang中Map按照Value大小排序的方法实例

    Golang中Map按照Value大小排序的方法实例

    这篇文章主要给大家介绍了关于Golang中Map按照Value大小排序的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-03-03
  • Go语言获取系统性能数据gopsutil库的操作

    Go语言获取系统性能数据gopsutil库的操作

    这篇文章主要介绍了Go语言获取系统性能数据gopsutil库的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Golang实现自己的Redis(TCP篇)实例探究

    Golang实现自己的Redis(TCP篇)实例探究

    这篇文章主要介绍了Golang实现自己的Redis(TCP篇)实例探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Go语言Gin处理响应方式详解

    Go语言Gin处理响应方式详解

    gin框架封装了常用的数据格式方法响应于客户端,下面这篇文章主要给大家介绍了关于Go语言Gin处理响应方式的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2023-01-01
  • golang grpc 负载均衡的方法

    golang grpc 负载均衡的方法

    这篇文章主要介绍了golang grpc 负载均衡的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • Golang Template实现自定义函数的操作指南

    Golang Template实现自定义函数的操作指南

    这篇文章主要为大家详细介绍了Golang如何利用Template实现自定义函数的操作,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-02-02

最新评论