在调用者线程中捕获线程的异常?

新手上路,请多包涵

一般来说,我对 Python 和多线程编程还很陌生。基本上,我有一个脚本可以将文件复制到另一个位置。我希望将其放置在另一个线程中,以便我可以输出 .... 以指示脚本仍在运行。

我遇到的问题是,如果无法复制文件,它将引发异常。如果在主线程中运行,这是可以的;但是,具有以下代码不起作用:

 try:
    threadClass = TheThread(param1, param2, etc.)
    threadClass.start()   ##### **Exception takes place here**
except:
    print "Caught an exception"

在线程类本身,我试图重新抛出异常,但它不起作用。我看到这里的人问过类似的问题,但他们似乎都在做比我想做的更具体的事情(而且我不太明白所提供的解决方案)。我见过有人提到 sys.exc_info() 的用法,但我不知道在哪里或如何使用它。

编辑: 线程类的代码如下:

 class TheThread(threading.Thread):
    def __init__(self, sourceFolder, destFolder):
        threading.Thread.__init__(self)
        self.sourceFolder = sourceFolder
        self.destFolder = destFolder

    def run(self):
        try:
           shul.copytree(self.sourceFolder, self.destFolder)
        except:
           raise

原文由 Phanto 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 299
2 个回答

问题是 thread_obj.start() 立即返回。您生成的子线程在其自己的上下文中执行,具有自己的堆栈。那里发生的任何异常都在子线程的上下文中,并且在它自己的堆栈中。我现在能想到的将此信息传递给父线程的一种方法是使用某种消息传递,因此您可能会研究一下。

试试这个尺寸:

 import sys
import threading
import queue

class ExcThread(threading.Thread):

    def __init__(self, bucket):
        threading.Thread.__init__(self)
        self.bucket = bucket

    def run(self):
        try:
            raise Exception('An error occured here.')
        except Exception:
            self.bucket.put(sys.exc_info())

def main():
    bucket = queue.Queue()
    thread_obj = ExcThread(bucket)
    thread_obj.start()

    while True:
        try:
            exc = bucket.get(block=False)
        except queue.Empty:
            pass
        else:
            exc_type, exc_obj, exc_trace = exc
            # deal with the exception
            print exc_type, exc_obj
            print exc_trace

        thread_obj.join(0.1)
        if thread_obj.isAlive():
            continue
        else:
            break

if __name__ == '__main__':
    main()

原文由 Santa 发布,翻译遵循 CC BY-SA 4.0 许可协议

这个问题有很多非常复杂的答案。我是否过于简单化了这一点,因为对我来说这对大多数事情来说似乎已经足够了。

 from threading import Thread

class PropagatingThread(Thread):
    def run(self):
        self.exc = None
        try:
            if hasattr(self, '_Thread__target'):
                # Thread uses name mangling prior to Python 3.
                self.ret = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            else:
                self.ret = self._target(*self._args, **self._kwargs)
        except BaseException as e:
            self.exc = e

    def join(self, timeout=None):
        super(PropagatingThread, self).join(timeout)
        if self.exc:
            raise self.exc
        return self.ret

如果你确定你只会在一个或另一个版本的 Python 上运行,你可以将 run() 方法减少到只有损坏的版本(如果你只会在以下版本上运行Python 3 之前),或者只是干净的版本(如果你只在 Python 3 开始的版本上运行)。

用法示例:

 def f(*args, **kwargs):
    print(args)
    print(kwargs)
    raise Exception('I suck at this')

t = PropagatingThread(target=f, args=(5,), kwargs={'hello':'world'})
t.start()
t.join()

当您加入时,您会看到在另一个线程上引发的异常。

如果您正在使用 six 或仅在 Python 3 上,您可以改进重新引发异常时获得的堆栈跟踪信息。您可以将内部异常包装在一个新的外部异常中,而不仅仅是连接点的堆栈,并获得两个堆栈跟踪

six.raise_from(RuntimeError('Exception in thread'),self.exc)

要么

raise RuntimeError('Exception in thread') from self.exc

原文由 ArtOfWarfare 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题