一起重温Golang中的异常处理啊????
- 1.Golang语言中没有其他语言中的
try...catch...
语句来捕获异常和异常恢复 - 2.在Golang中我们通常会使用
panic
关键字来抛出异常,在defer
中使用recover
来捕获异常进行具体逻辑处理 - 3.Golang中我们通常会在函数或方法中返回
error
结构对象来判断是否有异常出现
注意事项
- 1.利用
recover
和panic
指令,defer
必须放在panic之前定义(panic会终止其后要执行的代码
). - 2.
recover
只有在defer
调用的函数中才有效,否则recover
无法捕获到panic
. - 3.
recover
处理异常后,业务逻辑会跑到defer
之后的处理片段中 - 4.多个
defer
会形成defer栈
- 5.panic会等到整个
goroutine
退出才会报告错误
常规使用
-
panic
以及recover
参数类型为空接口(可存储任何类型对象)interface{}
/*
func panic(v interface{})
func recover() interface{}
执行顺序:panic()->带recover的defer
输出结果:
oh my god!panic.
解释:
defer中的recover成功捕获到了panic的异常
*/
package main
import (
"fmt"
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("oh my god!panic.")
}
- 延迟调用中引发的错误,可被后续延迟调用捕获(
仅最后一个错误被捕获
)
/*
执行顺序:panic()->带panic的defer匿名函数->带recover()的defer匿名函数
输出结果:
catch the panic
解释:
defer中的recover仅能捕获最后一个错误
package main
import (
"fmt"
)
func main() {
defer func() {
if err := recover();err != nil {
fmt.Println("catch the panic")
}
}()
defer func() {
panic("oh my god! panic.")
}()
panic("something panic!")
}
- 捕获函数
recover()
只有在defer
调用内直接调用才会终止,否则返回nil
/*
代码执行顺序:panic->在匿名函数中嵌套recover的defer函数->带fmt的defer->带recover的defer->在匿名函数中调用recover的defer
输出结果:
defer inner
<nil>
defer recover panic error
解释: 多个defer之间形成defer栈,最底部的defer优先执行;第三个defer打印了recover()的零值`nil`,仅有第一个defer成功捕获了最底部的panic("panic error")
*/
package main
import "fmt"
func main() {
defer func() {
fmt.Println("defer recover",recover())
}()
defer recover()
defer fmt.Println(recover())
defer func() {
func(){
fmt.Println("defer inner")
recover()
}()
}()
panic("panic error")
}
- 将代码块放置在匿名函数中可实现在函数逻辑中进行异常恢复,而不影响主函数
/*
代码执行顺序:匿名函数中的panic语句->匿名函数中i自加运算->匿名函数中的fmt->匿名函数中的defer->主函数中的fmt
输出结果:
i is: 2
解释:panic会终止其之后的执行,因此优先执行匿名函数中的panic之后便被defer中的recover捕获,将i赋值为2,其后匿名函数退出开始继续执行主函数中的fmt.Println语句
*/
package main
import "fmt"
func main() {
test()
}
func test() {
var i int
func() {
defer func(){
if err := recover();err != nil {
i = 2
}
}()
panic("something panic!")
i += 8
fmt.Println("no panic, i is:",i)
}()
fmt.Println("i is:",i)
}
- goroutine中的recover
注意:
如果一个没有recover
的goroutine
发生了panic,那么整个进程都会挂掉
/*
sync.WaitGroup用来等待一组goroutine的结束,Add方法用来设置等待的goroutine数量,Done方法表示一个goroutine运行结束,使用Wait方法将全部的goroutine阻塞住,直到全部goroutine执行完毕
代码执行顺序:goroutine中的逻辑->wg.Wait()->fmt.Println
输出结果:
panic recover assignment to entry in nil map
donw
解释:
在goroutine中我们声明了一个info的map[string]string类型,我们都知道在map,slice,channel都是引用类型,需要使用make函数进行初始化操作之后进行赋值。而这里直接使用info["name"] = "BGBiao"进行赋值导致panic,fmt.Println函数就会被终止执行,从而执行带recover的defer,之后执行带wg.Done()的defer并退出goroutine执行主程序逻辑
*/
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(4)
go func() {
defer wg.Done()
defer func() {
if err := recover();err != nil {
fmt.Println("panic recover",err)
}
}()
var info map[string]string
info["name"] = "BGBiao"
fmt.Println(info)
}()
wg.Wait()
fmt.Println("done")
}
欢迎关注我的公众号: BGBiao,一起进步~
有疑问加站长微信联系(非本文作者)