在主线程中引发一个线程中未处理的异常?

16

有一些类似的问题,但没有一个能提供我需要的答案。

如果我通过threading.Thread创建线程,然后这些线程抛出未处理的异常,那么这些线程将被终止。我希望保留异常详细信息和堆栈跟踪的默认打印输出,同时也使整个进程崩溃。

我考虑过在线程中捕获所有异常,并在主线程对象上重新引发它们,或者手动执行默认的异常处理,然后在主线程上引发SystemExit

最佳方法是什么?

3个回答

20

我写了关于在Python中重新抛出异常的文章,其中包括一个类似于以下示例的内容。

在你的工作线程中,你可以这样做(Python 3.x,请参见下面的Python 2.x版本):

try:
    self.result = self.do_something_dangerous()
except Exception as e:
    import sys
    self.exc_info = sys.exc_info()

并且在你的主线程上:

if self.exc_info:
    raise self.exc_info[1].with_traceback(self.exc_info[2])
return self.result

Python 2.x:

try:
    self.result = self.do_something_dangerous()
except Exception, e:
    import sys
    self.exc_info = sys.exc_info()

在主线程上,您需要执行以下操作:

if self.exc_info:
    raise self.exc_info[1], None, self.exc_info[2]
return self.result

异常会在主线程中出现,就像在工作线程中引发的一样。

你是不是想在主线程中抛出 thrdobj.exc_info,为什么重新抛出时要丢弃异常类型? - Matt Joiner
我现在明白了,不得不查阅一下关于3arg raise语句的资料。但是关于self的问题仍然存在,感谢你提供了一个好答案,我会尝试一下的。 - Matt Joiner
我不确定你关于self的问题是什么。这段代码来自一个用于将工作推迟到工作线程的对象,因此同一对象在工作线程和主线程中运行代码。 - Ned Batchelder
2
我怀疑问题不在于知道如何重新引发异常的机制,而在于何时重新引发异常,即主线程如何知道有一个异常等待重新引发? - Robert Rossney
这个答案 https://dev59.com/6HE85IYBdhLWcg3wVR6g#12223550 展示了一种很好的方式来封装你的线程实现,当主线程在工作线程上调用 join() 时,自动重新引发异常。 - ejm

14
唯一一个次要线程可以在主线程中可靠地引发的异常是KeyboardInterrupt:次要线程这样做的方式是调用函数thread.interrupt_main()。没有办法将额外的信息(关于异常原因)与被引发的异常对象相关联 - 后者总是一个普通的KeyboardInterrupt。因此,您需要将该信息存储在其他地方,例如专用实例的Queue.Queue上 - 该信息可能包括次要线程可以通过sys.exc_info()获得的结果,以及您找到的任何其他有用信息。
主线程将需要恢复该额外信息(并考虑到如果键盘中断实际上是由用户按下控制-C或类似按键引起,则队列将为空,因此使用get_nowait并准备处理Queue.Empty异常,例如),以任何所需的格式进行格式化,并终止(如果所有次要线程都是daemons,则当主线程终止时,整个进程终止)。

更新的线程文档链接:https://docs.python.org/3.6/library/_thread.html?highlight=interrupt_main#_thread.interrupt_main - user2682863

5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接