您好,登录后才能下订单哦!
这篇文章主要讲解了“Golang异常处理之怎么优雅地控制和处理异常”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Golang异常处理之怎么优雅地控制和处理异常”吧!
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情况下,程序会直接崩溃
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
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,否则会把整个程序弄崩溃
在使用 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异常处理之怎么优雅地控制和处理异常这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。