panic 问题

panic 问题

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 +0x46created by main.main
/home/wohu/GoCode/src/hello.go:17 +0x51exit status 2

> 可以看到,在协程 B 触发 panic 之后,协程 A 并没有继续打印,并且主协程的 main end 也没有打印出来,
>充分说明了在 B 协程触发 panic 之后,在 A 协程也会因此挂掉,且主协程也会挂掉。
>

#### 2.【问题2> 【问题2】如果协程 B 发生了 panic ,主协程 是否能用 recover 捕获到协程 B 的 panic ?
```cgo
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 +0x39created by main.main
/home/wohu/GoCode/src/hello.go:17 +0x59exit status 2

> 从结果可以看到, recover 并没有生效,所以我们可以下结论:哪个协程发生 panic,就需要在哪个协程自身中 recover 。
>                                   
> 协程A发生 panic ,协程B无法 recover 到协程A的 panic ,只有协程自己内部的 recover 才能捕获自己抛出的 panic 。
 #### 3. 业务开发实践
```cgo
package main

import (
 "donglei-go/common/helper" "fmt" "sync" "time")

func test3(ids ...int) string {
 return helper.InterfaceHelperObject.ToString(ids)}
// 该函数的参数为多个业务逻辑函数,且函数个数为变长参数
// func(string) string 这样写因为这里定义的事类型,不是具体的函数
func allTasks(params []string, tasks ...func(string) string) {
 //创建1个工作组 var wg sync.WaitGroup //t是函数参数 for index, t := range tasks { wg.Add(1) // 每启动一个协程等待组加 1 //函数以及参数传递到协程中 go func(f func(string) string,tmp string) { // 匿名函数的参数为业务逻辑函数 defer func() { // 在每个协程内部接收该协程自身抛出来的 panic if err := recover(); err != nil { fmt.Println("defer", err) } wg.Done() // 每个协程结束时给 等待组减 1 }() f(tmp) // 业务函数调用执行 }(t,params[index]) // 将当前的业务函数名传递给协程 } wg.Wait()}
// 业务逻辑 A
func A(params1 string) string {
 fmt.Println("A func begin"+" "+params1) panic("error A") return params1}
// 业务逻辑 B
func B(params2 string) string {
 fmt.Println("B func begin"+" "+params2) return params2}
func main() {
 test3(1,3,4) stringTmp := make([]string, 0) stringTmp = append(stringTmp,"aaaaaaaaa", "2222222") allTasks(stringTmp,A,B) // 将业务逻辑函数名 A B 传递给封装好的处理函数 time.Sleep(1 * time.Second) fmt.Println("main end")}

执行结果如下

/Users/myself/Golang/common_project/src/donglei-go <nil>
B func begin 2222222
A func begin aaaaaaaaa
defer error A
main end

Process finished with exit code 0
本作品采用《CC 协议》,转载必须注明作者和本文链接
good good study day day up
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!