【Go】panic/recover、编译器指令

https://blog.csdn.net/zhanggqianglovec/article/details/127906942
https://www.cnblogs.com/ricklz/p/14692264.html
https://zhuanlan.zhihu.com/p/624326431
学一点,整一点,基本都是综合别人的,弄成我能理解的内容

panic、recover

1、panic 能够改变程序的控制流,调用 panic 后会立刻停止执行当前函数的剩余代码,并在当前 Goroutine 中递归执行调用方的 defer(在panic()函数前面的已经执行过的defer语句);
2、recover 可以中止 panic 造成的程序崩溃。它是一个只能在 defer 中发挥作用的函数,在其他作用域中调用不会发挥作用;

1、panic 只会触发当前goroutine的defer
2、revoce 只有在defer中调用才能生效
3、panic 允许在defer中嵌套多次调用
4、recover没有传入参数,但是有返回值,返回值就是panic传递的值

使用场景

1、程序遇到无法执行下去的错误时,抛出错误,主动结束运行。
2、在调试程序时,通过 panic 来打印堆栈,方便定位错误。

error:可预见的错误
panic:不可预见的异常

需要注意的是,应该尽可能地使用error,而不是使用panic和recover。只有当程序不能继续运行的时候,才应该使用panic和recover机制。

panic有两个合理的用例。
1、发生了一个不能恢复的错误,此时程序不能继续运行。 一个例子就是 web 服务器无法绑定所要求的端口。在这种情况下,就应该使用 panic,因为如果不能绑定端口,啥也做不了。
2、发生了一个编程上的错误。 假如我们有一个接收指针参数的方法,而其他人使用 nil 作为参数调用了它。在这种情况下,我们可以使用panic,因为这是一个编程错误:用 nil 参数调用了一个只能接收合法指针的方法。

在一般情况下,我们不应通过调用panic函数来报告普通的错误,而应该只把它作为报告致命错误的一种方式。当某些不应该发生的场景发生时,我们就应该调用panic。

总结下panic的使用场景:
1、空指针引用
2、下标越界
3、除数为0
4、不应该出现的分支,比如default
5、输入不应该引起函数错误

示例

func main() {
	// 主线程中的defer函数并不会执行,因为子协程 panic后,主线程中的defer并不会执行
	defer println("in main")
 
	go func() {
		defer println("in goroutine")
		fmt.Println("子协程running")
		panic("子协程崩溃")
	}()
 
	time.Sleep(1 * time.Second)
}


//输出
子协程running
in goroutine
panic: 子协程崩溃
func main() {
	defer fmt.Println("in main")
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("occur error")
			fmt.Println(err)
		}
	}()
 
	panic("unknown err")
 
}

//输出
occur error
unknown err
in main

嵌套使用panic

func main() {
	defer fmt.Println("in main")
	defer func() {
		defer func() {
			panic("panic again and again")
		}()
		panic("panic again")
	}()
 
	panic("panic once")
}

//输出
in main
panic: panic once
        panic: panic again
        panic: panic again and again

panic源码

_panic定义

// _panic 保存了一个活跃的 panic
//
// 这个标记了 go:notinheap 因为 _panic 的值必须位于栈上
//
// argp 和 link 字段为栈指针,但在栈增长时不需要特殊处理:因为他们是指针类型且
// _panic 值只位于栈上,正常的栈指针调整会处理他们。
//
//go:notinheap
type _panic struct {
	argp      unsafe.Pointer // panic 期间 defer 调用参数的指针; 无法移动 - liblink 已知
	arg       interface{}    // panic的参数
	link      *_panic        // link 链接到更早的 panic
	recovered bool           // panic是否结束
	aborted   bool           // panic是否被忽略
}

https://www.cnblogs.com/ricklz/p/14692264.html

编译器指令

https://zhuanlan.zhihu.com/p/624326431

编译器指令是由 Go 编译器处理的,不会影响到代码运行时的行为。在 Go 中,编译器指令都是以 //go: 开头的注释形式。

//go:notinheap 是一个编译器指令,它告诉编译器一个类型不能被分配在堆上。它常常用于运行时包(runtime package)。用户代码通常不应使用这个指令。
//go:embed:这个在 Go 1.16 版本引入,它让你可以在编译时将文件或目录嵌入二进制文件中。
//go:generate:它是一个生成器的指令,你可以在注释后提供一个命令供 go generate 命令执行生成代码。
//go:noinline:这个指令阻止编译器内联一个函数。
//go:linkname:这个指令允许将一个局部函数链接到另一个函数。
//go:directives:你可以查看包含指令列表的文档。

//go:build是在Go 1.17中引入的新编译器指令,用于替代旧的// +build条件编译标签。此//go:build nosyn表示向编译器指示仅在存在nosyn标签时才构建特定的文件。
可以使用如下命令进行编译go build -tags=nosyn

//go:noescape 禁止变量逃逸
变量逃逸是指 编译器将自动地将超出自身生命周期的变量,从函数栈转移到堆中。 golang就是自动将函数外部引用的变量转义到堆栈中,从而保障其他地方使用不会为空或者异常。
如果变量不逃逸,GC压力就会很小,提升性能;

//go:norace 跳过竞态检测
由于Goroutine很方便写并行逻辑,但是往往我们再代码中需要通过go run -race xxx.go测试是否有竞态,会导致编译慢,如果加上norace就可以跳过;

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开心星人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值