Golang Go语言中关于 defer

发布于 1周前 作者 caililin 来自 Go语言

Golang Go语言中关于 defer

平时没注意,今天偶然发现子协程 panic 后,主协程的 defer 内的操作不会执行?要说捕捉不到子协程的异常可以理解,请问下有相关的文档说明这个现象的吗?

func Test_X(t *testing.T) {
	defer log.Println(111)
	go func() {
		panic(222)
	}()
}

Out

=== RUN   Test_X
panic: 222

更多关于Golang Go语言中关于 defer的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

22 回复

panic 后面加个 sleep,你应该能看到 111

更多关于Golang Go语言中关于 defer的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


不行的,都试过

主协程偶尔会先于子协程结束,因为协程的运行是异步的,主协程并不会去”等待“子协程的运行结束

要解决这个问题,使用 waitGroup 或者 channel & …

note:这不是一个 bug,而是协程以及异步本身的特性

我知道这不是 bug 。你可以用你说的方式让主协程的 defer 执行,并贴出你的代码。

应该是需要等待狗肉挺执行结束

贴出一个相同输出的片段:
<br>func Test_X(t *testing.T) {<br> defer func() {<br> log.Println(111)<br> }()<br> go func() {<br> panic(222)<br> }()<br> time.Sleep(time.Second)<br>}<br>

多运行几次,你就知道 defer 到底有没有执行过

不会,defer 就是这样设计的,只有在调用 defer 的 goroutine 中 panic 才会执行,其他 goroutine 没有提及,那么就是没有保证。
文档在 https://golang.org/ref/spec#Defer_statements

A “defer” statement invokes a function whose execution is deferred to the moment the surrounding function returns, either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.

文档指明了在“the corresponding goroutine” panic 的时候才会 defer 。

为了确定 panic 在一个 goroutine 中的行为,文档也描述了,在 https://golang.org/ref/spec#Handling_panics,我把它贴过来

While executing a function F, an explicit call to panic or a run-time panic terminates the execution of F. Any functions deferred by F are then executed as usual. Next, any deferred functions run by F’s caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking.

注意到"in the executing goroutine"的限制,那么就是说 panic 会触发在它所运行的 goroutine 中调用链上的 defer,然后整个程序就退出了。所以 panic 不会管其他 goroutine 中的 defer 。

给力,要的就是这个

话说那个小心心是怎么点出来的??网页端可以吗?

鼠标移到楼层号左边再左边的位置就会显示了

鼠标移到楼层号左边再左边的位置就会显示了

func main() {
defer log.Println(111)
go func() {
defer func() {
recover()
}()
panic(222)
}()
}

抱歉对你的问题有曲解,如 8#所言,defer 的执行条件是有 return,所以你需要 recover 以保障主协程中 return 的执行

铁子,可以试用下贴 gist 链接的功能,很棒嗷

谢谢推荐,有操作指南之类的东西吗?

你也许可以站内搜索一下,我明天也有可能发个教程

go 这种是对称协程,没有老爸儿子的概念。

defer … // 压栈
go func() {panic(222)}() // 子协程

main 协程和子协程执行顺序未知,都有可能先执行结束
main 先执行完:会执行 main 协程中的 defer
子协程先执行完:panic 信息

你测试一下


我之前的理解有问题。

输出结果有几种可能:
1. main 协程先于子协程结束
输出:111
2. 子协程先 panic,main 协程未执行到 println(111)
输出:panic 222 。。。
3. 子协程先 panic,main 协程执行到 println(111)
输出:
111
panic 222 。。。

2/3 是同一个逻辑,panic 忽略了上层的 defer

结论是:
1. 8 楼说的,panic 只处理当前协程的 defer 函数
2. panic 向上抛出

在Golang(Go语言)中,defer 关键字用于延迟一个函数或方法的执行,直到包含该 defer 语句的函数执行完毕时才会执行。defer 通常用于资源清理、文件关闭、解锁互斥锁等场景,以确保这些操作在函数返回前被正确执行,无论函数是正常返回还是由于错误提前返回。

使用 defer 时,需要注意以下几点:

  1. 栈行为defer 语句会按照后进先出的顺序执行,即最后 defer 的函数会最先执行。

  2. 参数求值defer 语句中的函数参数在 defer 语句执行时就会求值,而不是在延迟的函数执行时。这意味着如果参数是变量,那么会使用 defer 语句执行时的变量值。

  3. 错误处理:在错误处理中,defer 可以用来确保资源被释放,即使发生错误。例如,在打开文件或数据库连接后,可以使用 defer 来确保这些资源在函数结束时被关闭。

  4. 避免滥用:虽然 defer 强大且有用,但应避免滥用。过多的 defer 可能会使代码难以理解和维护。在可能的情况下,考虑使用其他资源管理策略,如上下文(context)或更明确的资源管理函数。

总之,defer 是 Go 语言中一个强大的特性,用于确保资源在函数结束时被正确释放。合理使用 defer 可以使代码更加健壮和易于维护。

回到顶部