zoukankan      html  css  js  c++  java
  • Golang: Panic、defer、Recover

    go - Should I use panic or return error? - Stack Overflow

    上述回答解释了panic与error的区别,一般来说panic用于会导致整个程序崩溃的错误(goroutine panic也会导致主程序panic),而error用于处理程序中可预见的常规错误。

    Defer, Panic, and Recover - go.dev

    panic/error语法差异:

    func ForEach(iterable interface{}, f interface{}) {
        if isNotIterable(iterable) {
            panic("Should pass in a slice or map!")
        }
    }
    func ForEach(iterable interface{}, f interface{}) error {
        if isNotIterable(iterable) {
            return fmt.Errorf("Should pass in a slice or map!")
        }
    }
    

    panic意味着这是一个严重到程序必须立即退出的大问题,而error是人为定义的错误,程序员可以通过if err != nil来处理error,但panic则会导致程序直接崩溃。

    golang没有try catch的语法,完全依靠if判断error;

    panic相当于直接raise Exception("panic info..."),而error仅用于打印或传递一个错误信息。但panic和exception又有所区别,我们在java/python可能会定义/使用一些exception,方便其他程序抓取,但在golang中却几乎不会主动去写一个panic,因为它意味着程序直接崩溃,通常我们只会被动使用recover抓取意料之外的panic,报错统一使用error传递和处理。

    Panic is a built-in function that stops the ordinary flow of control and begins panicking. When the function F calls panic, execution of F stops, any deferred functions in F are executed normally, and then F returns to its caller. To the caller, F then behaves like a call to panic. The process continues up the stack until all functions in the current goroutine have returned, at which point the program crashes. Panics can be initiated by invoking panic directly. They can also be caused by runtime errors, such as out-of-bounds array accesses.

    defer有些类似于其他语言里try catch中的finally,表示出现无论是否异常退出,都要执行的部分,通常用于open资源后的最终cleanup或者panic recover。

    defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.

    recover则类似于try catch语法,用于从panic中恢复,recover只能在defered function中调用(因为panic会退出程序,而defer会在程序退出前做一些操作,所以要在defer中调用recover(),当defer所在的函数panic后,recover会被调用,recover()的返回值就是panic()的内容,例如panic("panic")那么recover()就是panic字符。

    Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

    注意:panic recover逻辑中不能使用return语句,但是可以通过named return params来实现此需求,只需要在recover逻辑中修改返回变量的值即可。参考下述地址中的回答, 这里懒的示例了:

    Why does go panic recover to return value with local variable not work? - Stack Overflow

    一个panic recover的简单示例:

    package main
    
    import "log"
    
    func main() {
    	defer func() {
    		log.Println("done")  // Println executes normally even if there is a panic
    		if x := recover(); x != nil {
    			log.Printf("run time panic: %v", x)
    		}
    	}()
    	log.Println("start")
    	func() {
    		panic("child goroutine panics")
    	}()
    }
    

    当上述匿名函数panic之后,主函数的defer部分会被触发,由于使用recover()函数抓取了panic并且做了log.Printf的简单处理,因此当func() panic之后,主函数并不会崩溃,而是打印一句错误。

    补充:

    关于defer recover()抓取panic的层次问题?

    goroutine是golang并发的基本单位,使用defer recover方式可以实现对本函数内panic的try catch模式的处理,以便保证函数panic后不使程序异常退出。

    但如果函数内部开启了其他goroutine,而此类child goroutine panic之后,父函数的defer recover还有效吗?

    答案是:父函数的defer function依然会执行,但recover不生效。即程序依然会因为子函数的goroutine panic而崩溃,但会执行完父函数的defer function中除recover()之外的部分。

    例如我们把上述函数改为如下调用方式:

    package main
    
    import "log"
    
    func main() {
    	defer func() {
    		log.Println("done")  // Println executes normally even if there is a panic
    		if x := recover(); x != nil {
    			log.Printf("run time panic: %v", x)
    		}
    	}()
    	log.Println("start")
    	go func() {  // 唯一区别:这里加了go调用
    		panic("child goroutine panics")
    	}()
    }
    

    匿名函数在一个独立的child goroutine内执行,当匿名函数出现panic之后,父函数的defer(以及更上层的调用函数的defer部分)依然会执行,但只会打印“done”,由于recover()抓不到child goroutine的panic,因此整个程序会因为子函数goroutine的panic退出。

    本质原因是因为panic不能跨goroutine抓取,因此当想要从panic中recover时,尽可能的在底层goroutine中进行recover。

    想建一个数据库技术和编程技术的交流群,用于磨炼提升技术能力,目前主要专注于Golang和Python以及TiDB,MySQL数据库,群号:231338927,建群日期:2019.04.26,截止2021.02.01人数:300人 ... 如发现博客错误,可直接留言指正,感谢。
  • 相关阅读:
    个人作业——顶会热词进程1.3
    个人作业——顶会热词进程1.2
    每周总结(5.9)
    个人作业2——顶会热词进程1.1
    团队项目冲刺第10天
    CodeForces-1178F1 Short Colorful Strip 区间DP
    LOJ-2362 蚯蚓 队列优化
    Educational Codeforces Round 107 (Rated for Div. 2) G.Chips on a Board 倍增优化DP
    LOJ-2123 最短不公共子串 后缀自动机,子序列自动机
    LGR-084 C Reboot from Blue 贪心 DAG上DP最短路
  • 原文地址:https://www.cnblogs.com/realcp1018/p/15710200.html
Copyright © 2011-2022 走看看