打印Python中的守护线程异常
在Python中,如果在守护线程(daemon thread)中出现异常,它不会打印出错误追踪信息。
举个例子,这段代码创建了一个守护线程,并在新线程中抛出了一个异常:
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环境中导入并执行这段代码,无论线程是否是守护线程,异常信息都会显示出来。
根据文档,'daemon'标志的唯一意义是“当只剩下守护线程时,整个Python程序会退出。”这让我觉得在异常发生后不打印错误追踪信息可能是Python的一个bug,除非我在文档中漏掉了什么。
这真的是一个bug吗?还是我在文档中遗漏了什么,这种行为是故意的?如果这是故意的,我该如何在不使用IPython的情况下强制Python在守护线程中打印错误追踪信息呢?
2 个回答
这是个bug吗,还是我在文档中漏掉了什么,这种行为是故意的?
其实你自己已经说出了原因,只是没有意识到:
根据文档,'daemon'标志唯一的重要性在于“当只剩下守护线程时,整个Python程序会退出。”
如果你有一个非守护线程,Python会在你调用thread.start()
后等待这个线程完成。这种等待包括线程所做的任何事情,比如抛出和处理异常。
而如果你有一个守护线程,Python在调用thread.start()
后就不会等待它。相反,如果没有其他指令,Python会立即退出。这意味着你的线程根本没有机会去抛出或处理异常。
如果这是故意的,我该如何强制Python在守护线程中打印异常追踪信息,而不使用IPython?
对于一个daemon
线程来说,它应该做什么其实并不重要。如果它被指示去打印一些东西,结果也是一样的。
这也意味着你无法有条件地等待守护线程的某个动作。要么你把thread.daemon = False
,这样你就能得到所有的异常追踪信息,以及所有的打印、输入输出和其他操作。要么你把thread.daemon = True
,这样在其他线程都结束后,你就得不到任何异常追踪信息,也得不到打印、输入输出或其他操作。
有趣的是,如果我在IPython环境中导入代码执行,无论线程是否是守护线程,异常都会显示出来。
关于shell的一个特点是,它们不会退出,除非你强制结束它们。由于shell的解释器在等待你的输入时不会退出,所以任何启动的daemon
线程都会保持活着。
根据维基百科的定义,守护进程(daemon)应该和控制终端(tty)分离,所以我觉得没有异常信息显示是正确的(毕竟,守护进程即使在你关闭启动它的命令行后也应该继续工作)。
你可以在这里查看相关信息。
至于如何打印错误追踪信息,我觉得简单的用try/except然后记录到文件就可以解决这个问题了 :)