• 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 。

  • 相关阅读:
    【BZOJ3052】[wc2013]糖果公园 带修改的树上莫队
    【BZOJ4668】冷战 并查集
    HCNP学习笔记之史上最全华为路由器交换机配置命令大合集
    前端学习笔记之css清除浮动float的七种常用方法总结和兼容性处理
    前端学习笔记之CSS浮动浅析
    前端学习笔记之CSS文档流
    前端学习笔记之HTML/CSS 速写神器 Emmet
    js 基本类型与引用类型的存储
    Javascript何时执行
    Number使用笔记
  • 原文地址:https://www.cnblogs.com/wuchangblog/p/16393070.html
Copyright © 2020-2023  润新知