Golang异常处理之优雅地控制和处理异常

目录
  • panic和recover使用
  • 使用示例
  • 注意
  • 子函数panic主函数recover
  • 子协程panic主函数recover
  • 使用总结
  • 使用panic的几点担心
    • 性能
    • 性能对比
    • 安全

panic和recover使用

Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。在Go语言中,设计者们推荐使用多值返回来返回错误。遇到真正的异常的情况下(比如除数为 0了)。才使用Go中引入的Exception处理:defer, panic, recover。

这几个异常的使用场景可以这么简单描述:Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理

使用示例

package main
import "fmt"
func main(){
    fmt.Println("c")
     defer func(){ // 必须要先声明defer,否则不能捕获到panic异常
        fmt.Println("d")
        if err:=recover();err!=nil{
            fmt.Println(err) // 这里的err其实就是panic传入的内容,55
        }
        fmt.Println("e")
    }()
    f() //开始调用f
    fmt.Println("f") //这里开始下面代码不会再执行
}
func f(){
    fmt.Println("a")
    panic("异常信息")
    fmt.Println("b") //这里开始下面代码不会再执行
    fmt.Println("f")
}

输出结果:

c
a
d
异常信息
e

注意

  • 利用recover处理panic指令,recover需要定义在defer匿名函数内
  • defer需要在panic之前声明,否则当panic时,recover无法捕获到panic
  • panic无recover情况下,程序会直接崩溃

子函数panic主函数recover

func TestPanic(t *testing.T) {
	defer func() {
		if err := recover(); err != nil {
			println("recovered")
		}
	}()
	subFun()
	subFun()
}
func subFun() {
	println("subFun")
	panic("subFun panic")
}

输出结果如下,第一个sunFun后面的代码不会执行

subFun
recovered

子协程panic主函数recover

func subFun(i int) {
	fmt.Println("subFun,i=", i)
	panic("subFun panic")
}
func TestSubGoPanic(t *testing.T) {
	defer func() {
		if err := recover(); err != nil {
			println("recovered2")
		}
	}()
	go subFun(3)
	subFun(4)
	println("finish")
}

结果

subFun,i= 4
recovered2
subFun,i= 3
--- PASS: TestSubGoPanic (0.00s)
panic: subFun panic
goroutine 21 [running]:
zh.com/base/err.subFun(0x0?)
    /Users/albert/file/code/go/zh/gotest/base/err/panic_test.go:34 +0x89
created by zh.com/base/err.TestSubGoPanic
    /Users/albert/file/code/go/zh/gotest/base/err/panic_test.go:43 +0x46

recover会执行,但是程序崩溃了

使用总结

如果 panic 和 recover 发生在同一个协程,那么 recover 是可以捕获的,如果 panic 和 recover 发生在不同的协程,那么 recover 是不可以捕获的

也就是哪个协程有panic,哪个协程里必须要有recover,否则会把整个程序弄崩溃

使用panic的几点担心

性能

在使用 Golang 进行开发时,遇到 panic 是非常常见的情况。但是,panic 对于性能的影响是相对较小的,尤其是在实际使用中。

首先,Golang 在运行时会维护一个 panic 堆,用于存储栈中的 panic 对象。当程序遇到 panic 时,会将该 panic 对象添加到 panic 堆中。panic 堆的大小是有限的,如果堆中的对象过多,可能会导致 panic 堆溢出,从而影响程序的性能

性能对比

func BenchmarkSubFunWithError(b *testing.B) {
	for i := 0; i < b.N; i++ {
		go subFunWithError(i)
	}
}
func BenchmarkSubFunWithRecover(b *testing.B) {
	for i := 0; i < b.N; i++ {
		go subFunWithRecover(i)
	}
}
func subFunWithRecover(i int) {
	//fmt.Println("subFun,i=", i)
	defer func() {
		if error := recover(); error != nil {
			//println("subFunWithRecover_recovered")
		}
	}()
	time.Sleep(time.Second)
	panic("subFun panic")
}
func subFunWithError(i int) error {
	//fmt.Println("subFun,i=", i)
	time.Sleep(time.Second)
	return errors.New("subFunWithError")
}
BenchmarkSubFunWithError-12               673920              1992 ns/op             489 B/op          3 allocs/op
BenchmarkSubFunWithRecover-12            1000000              1229 ns/op             240 B/op          2 allocs/op

反而使用panic的性能更好?

安全

另外一个比较担心的点是panic容易导致崩溃,但是如上所示,只要main方法里做好recover,每个go协程使用封装好的带recover的方法来调用,其实并不会有问题

到此这篇关于Golang异常处理之优雅地控制和处理异常的文章就介绍到这了,更多相关Golang异常处理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 深入理解golang的异常处理机制

    前言 众所周知在java或php等很多面向对象的语言中, 异常处理是依靠throw.catch来进行的.在go语言中,panic和recover函数在作用层面分别对等throw和catch语句,当然也存在不同之处.下面话不多说,来一起看看详细的介绍吧. 从设计层面来看,panic和recover函数适用于那些真正的异常(例如整数除0),而throw catch finally机制常常被用来处理一些业务层面的自定义异常.因此在go语言中,panic和recover要慎用. 上述两种异常机制的使用中

  • Golang中panic的异常处理

    目录 前言 如何恢复panic造成的程序崩溃 何时使用panic 前言 Golang中当程序发生致命异常时(比如数组下标越界,注意这里的异常并不是error),Golang程序会panic(运行时恐慌).当程序发生panic时,程序会执行当前栈中的defer 函数列表.然后打印引发panic的具体信息,最后进程退出,本篇文章我们一起探讨Golang中的panic以及如何利用defer 和 recover 来恢复这种致命的异常 分析造成panic堆栈信息 func main() {     f1(

  • 小学生也能看懂的Golang异常处理recover panic

    🌌 专注Golang,Python语言,云原生,人工智能领域得博主 💜 过去经历的意义在于引导你,而非定义你, 💜 只要我们足够努力,任何人都有无限潜力 🚀panic 抛出异常函数 🚀recover 捕获异常函数 📣1:在一个主协成内捕获异常 package main import ( "fmt" ) func main(){ defer func(){ err := recover() if err != nil{ fmt.Println("捕获到异常") } }() panic("异常出现") //抛出异常,代表错误代码 } 🚀运行结果 📣2:假设子协成内部错误,看看主协成能不能捕获到 package main import ( "fmt" ) func Calculate(){ panic("异常出现") // 同样代表错误代码 } func main(){ defer func()

  • Golang异常处理之defer,panic,recover的使用详解

    目录 延迟是什么 延迟函数 延迟⽅法 延迟参数 堆栈的推迟 延迟的应⽤ panic和recover(宕机和宕机恢复) panic和recover机制 示例代码 延迟是什么 defer即延迟语句,极个别的情况下,Go才使⽤defer.panic.recover这种异常处理形式. defer可以延迟函数.延迟⽅法.延迟参数. 延迟函数 可以在函数中添加多个defer语句. 当函数执⾏到最后时,这些defer语句会按照逆序执⾏,最后该函数返回.特别是当你在进⾏⼀些打开资源的操作时,遇到错误需要提前返回

  • Golang中异常处理机制详解

    前言 通常我们需要编写好的错误处理方式,在了避免某些程序员滥用异常,于是Go这里直接把异常这一块给砍掉了,最终还是通过返回值来判断程序的异常情况,毕竟Go可是支持多返回值的语言,比如atoi.itoa等函数,就不能忽略它的第二个返回值,因为第二个返回值代表了转换是否成功!不过Golang还是提供了一些错误处理机制的 Go的错误机制 1.没有异常机制 2.error类型实现了error接口 3.可以通过errors.New来快速创建错误实例 type error interface{ Error(

  • Golang异常处理之优雅地控制和处理异常

    目录 panic和recover使用 使用示例 注意 子函数panic主函数recover 子协程panic主函数recover 使用总结 使用panic的几点担心 性能 性能对比 安全 panic和recover使用 Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱.在Go语言中,设计者们推荐使用多值返回来返回错误.遇到真正的异常的情况下(比如除数为 0了).才使用Go中引入的Exception处理

  • 一文教你如何优雅的控制全局loading的显示

    在很多后台管理系统中,发送请求的时候,需要打开一个loading,收到响应后,需要关闭这个loading,对于这种通用的逻辑,我一般是在axios拦截器中做这种处理,因为不是每个请求都需要全局显示loading,所以我在axios config中添加了一个标记showLoading, 用于标记发送请求之前是否需要显示loading,自然收到响应后,根据这个标记确定是否需要关闭loading,在axios拦截器中的代码如下: axios.interceptors.request.use( (con

  • Golang实现程序优雅退出的方法详解

    目录 1. 背景 2. 常见的几种平滑关闭 2.1 http server 平滑关闭 2.2 gRPC server 平滑关闭 2.3 worker 协程平滑关闭 2.4 实现 io.Closer 接口的自定义服务平滑关闭 2.5 集成其他框架怎么做 1. 背景 项目开发过程中,随着需求的迭代,代码的发布会频繁进行,在发布过程中,如何让程序做到优雅的退出? 为什么需要优雅的退出? 你的 http 服务,监听端口没有关闭,客户的请求发过来了,但处理了一半,可能造成脏数据. 你的协程 worker

  • Java 如何优雅的抛出业务异常

    记得上学的时候学习英语,每个英语老师说到英语翻译的时候都会说英语翻译要做到"信.达.雅".如今做了一名程序员竟然体会我还是想用这三种境界来要求自己,恰逢自己现在所做之项目偏业务,代码的优雅显得格外重要.所以我就想写点这方面的东西,今天就先来说说如何优雅的抛出业务异常.代码千千万,只希望对自己和对大家有所帮助. 针对java开发者而言,异常的重要性不言而喻,这里也不再赘述.今天主要说说在业务中利用异常处理机制来实现业务异常的优雅提示. 首先,我们定义一个异常编码接口 public int

  • golang的httpserver优雅重启方法详解

    前言 去年在做golangserver的时候,内部比较头疼的就是在线服务发布的时候,大量用户的请求在发布时候会被重连,在那时候也想了n多的方法,最后还是落在一个github上的项目,facebook的一个golang项目grace,那时候简单研究测试了一下可以就直接在内部使用了起来,这段时间突然想起来,又想仔细研究一下这个项目了. 从原理上来说是这样一个过程: 1)发布新的bin文件去覆盖老的bin文件 2)发送一个信号量,告诉正在运行的进程,进行重启 3)正在运行的进程收到信号后,会以子进程的

  • 如何优雅的处理Spring Boot异常信息详解

    Spring Boot 异常处理 异常处理是一种识别并响应错误的一致性机制,异常机制可以把程序中的异常处理代码和正常的业务逻辑代码分离,包装程序的可读性和健壮性.在Spring Boot应用程序中,能够捕获并及时的响应客户端的错误操作是一件非常重要的事情.在本章节中,我将展示如何处理Spring Boot中的异常. 1. 相关注解说明 在进行演示之前,我们先了解一下在Spring Boot应用程序中与异常处理相关的几个注解 注解名称 说明 @ControllerAdvice 该标签用于处理全局的

  • 优雅的处理vue项目异常实战记录

    背景 你还在为处理Uncaught (in promise) ReferenceError烦恼吗? 你还在为捕获异常反复的写try catch吗? 你还在为每一个promise写catch吗? 是时候一站式统一处理异常!!!(针对vue项目) 全局异常捕获 Vue.config.errorHandler = function (err, vm, info) { // 指定组件的渲染和观察期间未捕获错误的处理函数.这个处理函数被调用时,可获取错误信息和 Vue 实例. // handle erro

  • 解析C#中断言与异常的应用方式及异常处理的流程控制

    断言与异常(Assertion Vs Exception) 在日常编程实践中,断言与异常的界限不是很明显,这也使得它们常常没有被正确的使用.我也在不断的与这个模糊的怪兽搏斗,仅写此文和大家分享一下我的个人看法.我想我们还可以从很多角度来区别断言和异常的使用场景,欢迎大家的意见和建议. 异常的使用场景:用于捕获外部的可能错误 断言的使用场景:用于捕获内部的不可能错误 我们可以先仔细分析一下我们在.net中已经存在的异常. System.IO.FileLoadException SqlExcepti

随机推荐