在Python中打印守护线程异常

9

Python不会打印在守护线程中引发的异常的回溯信息。

例如,以下代码创建了一个守护线程并在新线程中引发异常:

def error_raiser():
    raise Exception

import threading
thread = threading.Thread(target=error_raiser)
thread.daemon = True
thread.start()

但是不会打印回溯信息(不输出任何内容)。

然而,如果线程没有设置为守护线程,Python 将会打印回溯信息。以下是同样的代码,只是注释掉了一行:

def error_raiser():
    raise Exception

import threading
thread = threading.Thread(target=error_raiser)
# thread.daemon = True
thread.start()

输出结果为:

Exception in Thread-1:
Traceback (most recent call last):
  File "C:\Python26\lib\threading.py", line 525, in __bootstrap_inner
    self.run()
  File "C:\Python26\lib\threading.py", line 477, in run
    self.__target(*self.__args, **self.__kwargs)
  File "test.py", line 2, in error_raiser
    raise Exception
Exception

在Python 2.6.2和Python 3.0.1中执行此代码会得到相同的结果。有趣的是,如果我在IPython shell中导入代码并执行,无论线程是否为守护进程,都会显示异常。

根据文档,'daemon'标志的唯一意义是"当只剩下守护线程时,整个Python程序退出"。这让我相信,在异常后不打印回溯信息是Python中的一个错误,除非我在文档中漏掉了什么。

这是一个bug吗?还是我在文档中漏掉了什么,这种行为是有意的?如果是有意的,如何在不使用IPython的情况下强制Python打印守护线程中的回溯信息?

2个回答

8
根据维基百科的定义,守护进程应该与控制tty分离,所以我认为不显示异常是正确的(毕竟,即使关闭启动它的shell,守护进程也应该继续工作)..
请参见此处
至于如何打印回溯信息,我认为一个简单的try/except_then_log_to_file就可以解决问题 :)

1
你的解决方案非常适用于打印回溯信息。我没有想到使用try...except,因为我依赖Python来为我打印。顺便说一下,根据文档,我相信守护线程在程序退出时会被终止,而不仅仅是分离。感谢您的帮助! - user94070

3
这是一个bug吗?还是我在文档中漏掉了什么,这种行为是有意的?
你已经自己说明了原因,却没有意识到:
根据文档,“守护进程”标志的唯一意义是“只有守护线程剩下时,整个Python程序才会退出”。
如果你有一个非守护线程,在`thread.start()`之后Python会等待它。这个等待包括它所做的任何事情,包括引发和处理异常。
如果你有一个守护线程,在`thread.start()`之后,Python不会等待它。相反,在没有进一步的指示的情况下,Python立即退出。这意味着你的线程永远没有机会引发或处理异常。
如果这是有意的,那么我如何强制Python在守护线程中打印回溯信息而不使用IPython?
对于一个守护线程,你的线程应该做什么是无关紧要的。如果它被指示打印某些内容,同样的事情也会发生。
这也意味着你没有办法有条件地等待守护线程的操作。要么将`thread.daemon = False`,并获得所有回溯信息 - 以及所有打印、I/O和其他操作。要么将`thread.daemon = True`,在所有其他线程死亡后,不会获得回溯信息 - 也不会有打印、I/O或其他操作。
有趣的是,如果我通过在IPython shell中导入代码来执行它,无论线程是否是守护进程,异常都会被显示出来。
关于shell的事情是,除非你杀死它们,否则它们永远不会退出。由于shell的解释器在等待你的输入时不会退出,任何启动的`daemon`线程都会保持活动状态。

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