golang之panic

2024-02-12 09:06:58 浏览数 (2)

在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退出程序。

0 人点赞