Go 多协程(单个协程触发panic会导致所有协程挂掉,每个协程只能捕获到自己的 panic 不能捕获其它协程)
一、概念
在多协程并发环境下,我们常常会碰到以下两个问题。假设我们现在有 2 个协程,我们叫它们协程 A 和 B 。
【问题1】如果协程 A 发生了 panic ,协程 B 是否会因为协程 A 的 panic 而挂掉?
【问题2】如果协程 A 发生了 panic ,协程 B 是否能用 recover 捕获到协程 A 的 panic ?
答案分别是:会、不能。
1.【问题1】
- 【问题1】如果协程 A 发生了
panic
,协程 B 是否会因为协程 A 的panic
而挂掉?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package main import ( "fmt" "time" ) func main() { // 协程 A go func () { for { fmt.Println( "协程 A" ) } }() // 协程 B go func () { time.Sleep(1 * time.Microsecond) // 确保 协程 A 先运行起来 panic( "协程 B panic" ) }() time.Sleep(10 * time.Second) // 充分等待协程 B 触发 panic 完成和协程 A 执行完毕 fmt.Println( "main end" ) } |
输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 协程 A 协程 A 协程 A 协程 A 协程 A 协程 A 协程 A 协程 A 协程 A 协程 A panic: 协程 B panic goroutine 6 [running]: main.main.func2() /home/wohu/GoCode/src/hello. go :19 +0x46 created by main.main /home/wohu/GoCode/src/hello. go :17 +0x51 exit status 2 |
可以看到,在协程 B 触发 panic 之后,协程 A 并没有继续打印,并且主协程的 main end
也没有打印出来,充分说明了在 B 协程触发 panic 之后,
在 A 协程也会因此挂掉,且主协程也会挂掉
2.【问题2】
- 【问题2】如果协程 A 发生了
panic
,协程 B 是否能用recover
捕获到协程 A 的panic
?
精简上面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | package main import ( "fmt" "time" ) func main() { defer func () { if err := recover(); err != nil { fmt.Printf( "panic err is %s" , err) } }() // 协程 B go func () { panic( "协程 B panic" ) }() time.Sleep(1 * time.Second) // 充分等待协程 B 触发 panic 完成 fmt.Println( "main end" ) } |
我们开启 1 个协程 B,并在主协程中增加 recover
机制,尝试在主协程中捕获协程 B 触发的 panic
, 但是结果未能如愿。
打印结果如下:
1 2 3 4 5 6 7 8 | panic: 协程 B panic goroutine 5 [running]: main.main.func2() /home/wohu/GoCode/src/hello. go :18 +0x39 created by main.main /home/wohu/GoCode/src/hello. go :17 +0x59 exit status 2 |
从结果可以看到, recover
并没有生效,所以我们可以下结论:
1 | 哪个协程发生 panic,就需要在哪个协程自身中 recover 。 |
改成如下代码,可以正常 recover。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package main import ( "fmt" "time" ) func main() { go func () { defer func () { if err := recover(); err != nil { fmt.Printf( "panic err is %s\n" , err) } }() panic( "panic" ) }() time.Sleep(1 * time.Second) // 充分等待协程触发 panic 完成 fmt.Println( "main end" ) } |
输出结果:
1 2 | panic err is panic main end |
所以结论如下:
协程A发生 panic
,协程B无法 recover
到协程A的 panic
,只有协程自己内部的 recover
才能捕获自己抛出的 panic
。
感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮。本文欢迎各位转载,但是转载文章之后必须在文章页面中给出作者和原文连接。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
2021-06-20 struct解析为json
2021-06-20 golang panic和recover
2021-06-20 fmt.sprintf,fprintf区别
2021-06-20 web界面不显示内容排查思路