在go语言中,panic是一种用于处理不可恢复错误和异常情况的机制。大多数情况下,我们用panic来快速解决正常运行中出现的异常情况,或者我们没有准备好优雅地处理的错误。
源码版本:go1.21.4
panic源码
runtime/runtime2.go
代码语言:javascript复制type _panic struct {
argp unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink 指针指向panic期间运行的延迟调用的参数;
arg any // argument to panic //参数
link *_panic // link to earlier panic 链接下一个 panic 结构体
pc uintptr // where to return to in runtime if this panic is bypassed 如果绕过此panic,则在运行时返回到何处
sp unsafe.Pointer // where to return to in runtime if this panic is bypassed
recovered bool // whether this panic is over panic是否已经结束?
aborted bool // the panic was aborted panic停止了
goexit bool
}
触发panic条件
1、用panic函数主动触发
代码语言:javascript复制package main
import "fmt"
func main(){
panic("异常")
fmt.Println("end")
}
输出:
代码语言:javascript复制panic: 异常
2、操作初始化map,slice等
代码语言:javascript复制package main
func main(){
//示例1
//var d map[string]string
//d["a"]="a"
//示例2
//var c []string
//c[1]="aa"
//示例3
var h chan int
close(h)
}
捕获panic信息
通过defer和recover捕获
代码语言:javascript复制package main
import "fmt"
func main(){
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("被捕获错误信息")
fmt.Println("hello")
}
多个panic情况
示例1
代码语言:javascript复制package main
import "fmt"
func main(){
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("被捕获错误信息1")
panic("被捕获错误信息2")
fmt.Println("hello")
}
输出:被捕获错误信息1
因为执行第一个panic就结束程序,不会执行后面程序,所以不会只执行第二个panic 示例2
代码语言:javascript复制package main
import "fmt"
func main(){
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer panic("被捕获错误信息1")
panic("被捕获错误信息2")
fmt.Println("hello")
}
输出:被捕获错误信息1
这个和上面一样也不会执行第二个panic 示例3
代码语言:javascript复制package main
import "fmt"
func main(){
defer panic("被捕获错误信息1")
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
panic("被捕获错误信息2")
fmt.Println("hello")
}
输出:
代码语言:javascript复制被捕获错误信息2
panic: 被捕获错误信息1
先进入defer的 panic,会被执行 示例4
代码语言:javascript复制package main
import "fmt"
func main(){
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
a:=test()
fmt.Println(a)
panic("被捕获错误信息1")
fmt.Println("hello")
}
func test() (a int) {
a=0
panic("被捕获错误信息2")
return
}
输出:被捕获错误信息2 示例5
代码语言:javascript复制package main
import "fmt"
func main(){
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
go func() {
panic("被捕获错误信息1")
}()
}
没有捕捉到panic,说明不同的goroutine,不共享defer 示例6
代码语言:javascript复制package main
func main(){
defer panic("被捕获错误信息1")
defer panic("被捕获错误信息2")
defer panic("被捕获错误信息3")
}
输出:
代码语言:javascript复制panic: 被捕获错误信息3
panic: 被捕获错误信息2
panic: 被捕获错误信息1
示例7
代码语言:javascript复制package main
import "fmt"
func main(){
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
defer panic("被捕获错误信息1")
defer panic("被捕获错误信息2")
defer panic("被捕获错误信息3")
}
输出:被捕获错误信息1 recover只捕获1其他的没有捕获,退出了程序
总结
1、defer、 recover、panic 三者之间顺序 recover放入defer,捕捉panic结束程序之前输出的信息。 2、defer 和recover必须一起才能捕捉panic,如果单独recover,放在panic之前,没有相应错误信息,捕捉不到,放在panic之后,则panic发出结束程序后,不会执行recover。 3、多个panic放入defer会都输出。 4、不同的goroutine,不能捕捉对方的panic信息,不共享defer中的队列。 5、recover只捕捉之后紧邻的panic信息。 6、recover除了捕捉panic信息外,还接管panic退出程序。