前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >go里面的异常处理

go里面的异常处理

原创
作者头像
Johns
修改2022-09-20 11:59:38
3550
修改2022-09-20 11:59:38
举报
文章被收录于专栏:代码工具

介绍

Go 1.x 没有结构化异常,使用 panic 抛出错误,recover 捕获错误。

Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

panic 内置函数

1) 假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行

2) 返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行直到goroutine整个退出,并报告错误

recover内置函数

1) 用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为

2) 在defer函数中,通过recover来终止一个goroutine的panicking过程,从而恢复正常代码的执行, 可以获取通过panic传递的error

注意:

  1. 利用recover处理panic指令,defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。否则当panic时,recover无法捕获到panic,无法防止panic扩散。
  2. recover 处理异常后,逻辑并不会恢复到 panic 那个点去,函数跑到 defer 之后的那个点。
  3. 多个 defer 会形成 defer 栈,后定义的 defer 语句会被最先调用。

异常按照轻重可以分为error和painc, 其中painc是指不可修复性错误, error指之外的其他错误,

一般error可以忽略或者不处理, 但是painc必须处理。

正确的Painc处理

代码语言:go
复制
package main

func main() {
    task()
}

func task() {
    defer func() {
        if err := recover(); err != nil {
            log.Error(err)
        }
    }()
    panic("panic error!")
}

如果不想在每个task方法里面都写相同的defer, 可以简单封装一下

代码语言:go
复制
func main(){
    var task2 = func() {
		panic("bad exec params")
	}

	// 示例1: 普通方法调用
	err := WithRecovery(task2, nil)
	if err != nil {
		fmt.Println(err)
	}

	// 示例2: 开新goroutine
	go WithRecovery(task2, func(r interface{}) {
		log.Error(context.Background(),
			zap.Reflect("panic in the recoverable goroutine", r),
			zap.Stack("panic stack info"),
		)
	})
}

// WithRecovery goroutine Recovery
// 如果没开新goroutine那么就直接用err
// 如果开新goroutine建议在recoverFn里面处理异常
func WithRecovery(execFunc func(), recoverFn func(r interface{})) (err error) {
	defer func() {
		if r := recover(); r != nil {
			stackBuf := make([]byte, 1024)
			n := runtime.Stack(stackBuf, false)
			err = errors.New(fmt.Sprintf("panic: %v %s", err, stackBuf[:n]))
			if recoverFn != nil {
				recoverFn(r)
			}
		}
	}()
	execFunc()
	return
}

需要注意的是 recover函数只能hold住当前goroutine的异常, 如果是从其他goroutine传递回来的它是hold不住的

代码语言:txt
复制
func main() {
	defer func() {
		if e := recover(); e != nil {
			log.Error(e)
		}
	}()
	var wg sync.WaitGroup
	wg.Add(1)

	var task = func() {
		// 这段注释后main函数会直接退出, 不会打印ok
		/*defer func() {
			if e := recover(); e != nil {
				log.Error(e)
				wg.Done()
			}
		}()*/
		panic("bad exec params")
		wg.Done()
	}
	go task()

	wg.Wait()
	fmt.Println("ok")
}

Go2草案

image.png
image.png
代码语言:txt
复制
func main() {
 handle err {
  log.Fatal(err)
 }

hex := check ioutil.ReadAll(os.Stdin)
}

该提案引入了两种新的语法形式,首先是 check 关键字,其可以选中一个表达式 check f(x, y, z)check err,其将会标识这是一个显式的错误检查。

其次引入了 handle 关键字,用于定义错误处理程序流转,逐级上抛,依此类推,直到处理程序执行 return 语句,才正式结束。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
    • panic 内置函数
      • recover内置函数
        • 注意:
    • 正确的Painc处理
    • Go2草案
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档