Tra*_*ggs 11 multithreading python-3.x
我有一个 python3 程序,它启动第二个线程(除了主线程)来异步处理一些事件。理想情况下,我的程序可以正常运行,并且永远不会有未处理的异常。但事情发生了。当/如果有异常,我希望整个解释器以错误代码退出,就好像它是单个线程一样。那可能吗?
现在,如果在生成的线程上发生异常,它会打印出通常的错误信息,但不会退出。主线程一直在运行。
import threading
import time
def countdown(initial):
while True:
print(initial[0])
initial = initial[1:]
time.sleep(1)
if __name__ == '__main__':
helper = threading.Thread(target=countdown, args=['failsoon'])
helper.start()
time.sleep(0.5)
#countdown('THISWILLTAKELONGERTOFAILBECAUSEITSMOREDATA')
countdown('FAST')
Run Code Online (Sandbox Code Playgroud)
该countdown最终将无法访问[0],因为它已经清空引起从字符串IndexError: string index out of range错误。目标是无论主程序还是辅助程序先死,整个程序都会死,但堆栈跟踪信息仍然输出。
经过一番挖掘,我的想法是使用sys.excepthook. 我添加了以下内容:
def killAll(etype, value, tb):
print('KILL ALL')
traceback.print_exception(etype, value, tb)
os.kill(os.getpid(), signal.SIGKILL)
sys.excepthook = killAll
Run Code Online (Sandbox Code Playgroud)
如果主线程是第一个死亡的线程,则此方法有效。但在另一种情况下则不然。这似乎是一个已知问题(https://bugs.python.org/issue1230540)。我会在那里尝试一些解决方法。
虽然该示例显示了我创建的一个主线程和一个辅助线程,但我对我可能正在运行启动线程的其他人的库的一般情况感兴趣。
好吧,您可以简单地在线程中引发错误,并让主线程处理并报告该错误。从那里您甚至可以终止该程序。
例如在你的工作线程上:
try:
self.result = self.do_something_dangerous()
except Exception as e:
import sys
self.exc_info = sys.exc_info()
Run Code Online (Sandbox Code Playgroud)
并在主线程上:
if self.exc_info:
raise self.exc_info[1].with_traceback(self.exc_info[2])
return self.result
Run Code Online (Sandbox Code Playgroud)
因此,为了给您一个更完整的了解,您的代码可能如下所示:
import threading
class ExcThread(threading.Thread):
def excRun(self):
pass
#Where your core program will run
def run(self):
self.exc = None
try:
# Possibly throws an exception
self.excRun()
except:
import sys
self.exc = sys.exc_info()
# Save details of the exception thrown
# DON'T rethrow,
# just complete the function such as storing
# variables or states as needed
def join(self):
threading.Thread.join(self)
if self.exc:
msg = "Thread '%s' threw an exception: %s" % (self.getName(), self.exc[1])
new_exc = Exception(msg)
raise new_exc.with_traceback(self.exc[2])
Run Code Online (Sandbox Code Playgroud)
(我添加了一行额外的行来跟踪哪个线程导致错误,以防您有多个线程,命名它们也是一个好习惯)
我的解决方案最终成为此处发布的解决方案与SIGKILL上面的解决方案之间的幸福结合。我将以下子killall.py模块添加到我的包中:
import threading
import sys
import traceback
import os
import signal
def sendKillSignal(etype, value, tb):
print('KILL ALL')
traceback.print_exception(etype, value, tb)
os.kill(os.getpid(), signal.SIGKILL)
original_init = threading.Thread.__init__
def patched_init(self, *args, **kwargs):
print("thread init'ed")
original_init(self, *args, **kwargs)
original_run = self.run
def patched_run(*args, **kw):
try:
original_run(*args, **kw)
except:
sys.excepthook(*sys.exc_info())
self.run = patched_run
def install():
sys.excepthook = sendKillSignal
threading.Thread.__init__ = patched_init
Run Code Online (Sandbox Code Playgroud)
然后install在启动任何其他线程(我自己创建的或来自其他导入的库)之前立即运行。
| 归档时间: |
|
| 查看次数: |
6237 次 |
| 最近记录: |