Поймать исключение потока в потоке вызывающего в Python - PullRequest
162 голосов
/ 13 мая 2010

Я очень плохо знаком с 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

Ответы [ 14 ]

0 голосов
/ 06 февраля 2019

Обтекание темы с сохранением исключений.

import threading
import sys
class ExcThread(threading.Thread):

    def __init__(self, target, args = None):
        self.args = args if args else []
        self.target = target
        self.exc = None
        threading.Thread.__init__(self)

    def run(self):
        try:
            self.target(*self.args)
            raise Exception('An error occured here.')
        except Exception:
            self.exc=sys.exc_info()

def main():
    def hello(name):
        print(!"Hello, {name}!")
    thread_obj = ExcThread(target=hello, args=("Jack"))
    thread_obj.start()

    thread_obj.join()
    exc = thread_obj.exc
    if exc:
        exc_type, exc_obj, exc_trace = exc
        print(exc_type, ':',exc_obj, ":", exc_trace)

main()
0 голосов
/ 01 июля 2017

Простой способ перехвата исключения потока и обратной связи с методом вызывающего может быть путем передачи словаря или списка методу worker.

Пример (передача словаря методу работника):

import threading

def my_method(throw_me):
    raise Exception(throw_me)

def worker(shared_obj, *args, **kwargs):
    try:
        shared_obj['target'](*args, **kwargs)
    except Exception as err:
        shared_obj['err'] = err

shared_obj = {'err':'', 'target': my_method}
throw_me = "Test"

th = threading.Thread(target=worker, args=(shared_obj, throw_me), kwargs={})
th.start()
th.join()

if shared_obj['err']:
    print(">>%s" % shared_obj['err'])
0 голосов
/ 12 августа 2014

Один метод, который мне нравится, основан на схеме наблюдателя . Я определяю класс сигнала, который мой поток использует для выдачи исключений слушателям. Он также может быть использован для возврата значений из потоков. Пример:

import threading

class Signal:
    def __init__(self):
        self._subscribers = list()

    def emit(self, *args, **kwargs):
        for func in self._subscribers:
            func(*args, **kwargs)

    def connect(self, func):
        self._subscribers.append(func)

    def disconnect(self, func):
        try:
            self._subscribers.remove(func)
        except ValueError:
            raise ValueError('Function {0} not removed from {1}'.format(func, self))


class WorkerThread(threading.Thread):

    def __init__(self, *args, **kwargs):
        super(WorkerThread, self).__init__(*args, **kwargs)
        self.Exception = Signal()
        self.Result = Signal()

    def run(self):
        if self._Thread__target is not None:
            try:
                self._return_value = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
            except Exception as e:
                self.Exception.emit(e)
            else:
                self.Result.emit(self._return_value)

if __name__ == '__main__':
    import time

    def handle_exception(exc):
        print exc.message

    def handle_result(res):
        print res

    def a():
        time.sleep(1)
        raise IOError('a failed')

    def b():
        time.sleep(2)
        return 'b returns'

    t = WorkerThread(target=a)
    t2 = WorkerThread(target=b)
    t.Exception.connect(handle_exception)
    t2.Result.connect(handle_result)
    t.start()
    t2.start()

    print 'Threads started'

    t.join()
    t2.join()
    print 'Done'

У меня недостаточно опыта работы с потоками, чтобы утверждать, что это абсолютно безопасный метод. Но это сработало для меня, и мне нравится гибкость.

0 голосов
/ 13 мая 2010

Использование обнаженных исключений не очень хорошая практика, потому что вы обычно ловите больше, чем ожидаете.

Я бы предложил изменить except, чтобы поймать ТОЛЬКО исключение, которое вы хотели бы обработать. Я не думаю, что повышение его имеет желаемый эффект, потому что когда вы идете для создания экземпляра TheThread во внешнем try, если это вызывает исключение, назначение никогда не произойдет.

Вместо этого вы можете просто предупредить об этом и двигаться дальше, например:

def run(self):
    try:
       shul.copytree(self.sourceFolder, self.destFolder)
    except OSError, err:
       print err

Затем, когда это исключение будет обнаружено, вы можете обработать его там. Затем, когда внешний try ловит исключение из TheThread, вы знаете, что это будет не то, что вы уже обработали, и поможет вам изолировать поток процесса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...