GO成神之路: Go异常处理(四)|Go主题月

780 阅读2分钟

异常处理与协程

在讨论这个问题之前我们应该明确:Go不支持跨协程处理异常

下面这个例子中,虽然在main中处理了异常但是异常并没有被捕获,所以这说明当前协程并不能处理其它协程中的panic,其实在Go异常处理(三)中我们已经提及了,gopanic函数中如果没有调用mcall退出函数,则在最后会调用fatalpanic函数从而间接触发exit函数而导致函数退出,这也就是为什么协程异常会导致整个程序崩溃。

package main

import (
	"log"
	"time"
)

func main() {
    defer func() {
        if err := recover(); err != nil {
                // 这段代码并不会被调用
                log.Println("main:",err)
        }
    }()
    // 在协程中抛出异常
    go func() {
        panic("error")
    }()

    time.Sleep(time.Second*10)
}

为什么不支持跨协程处理异常

在开始讨论之前,在此声明:以下所有的内容目前都是猜测,还没有验证这个猜想是否完全正确,这目前只是一个研究方向。

  • 猜测1:g对象并没有记录当前g关联的父g是哪个,所以没有办法将错误信息传递过去。
  • 猜测2:协程的执行是在不同的线程中执行,而且协程的执行并没有先后顺序,这也导致了无法确定抛出panic的协程是不是在父协程前执行完成的。
  • 猜测3:如果多个协程中都抛出了异常,但是协程的执行顺序又不固定,那Go怎么知道哪个异常应该是第一个被处理的呢?
  • 猜测4:当然如果g中保存了协程间的引用关系,自然知道异常的抛出顺序是什么的,那么这样做是不是会影响go的性能呢?
  • 猜测5:如果Go将所有的异常信息保存到一个全局的异常对象中,会不会因为频繁的lock导致性能降低呢?