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而挂掉?
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")
}
输出结果:
协程 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?
精简上面的代码:
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 , 但是结果未能如愿。
打印结果如下:
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 并没有生效,所以我们可以下结论:
哪个协程发生 panic,就需要在哪个协程自身中 recover 。
改成如下代码,可以正常 recover。
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")
}
输出结果:
panic err is panic main end
所以结论如下:
协程A发生 panic ,协程B无法 recover 到协程A的 panic ,只有协程自己内部的 recover 才能捕获自己抛出的 panic 。

浙公网安备 33010602011771号