Я очень плохо знаком с 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
TheThread
? Пример кода возможно?Ответы:
Проблема в том, что
thread_obj.start()
возвращается немедленно. Созданный вами дочерний поток выполняется в своем собственном контексте со своим собственным стеком. Любое исключение возникает в контексте дочернего потока и находится в его собственном стеке. Прямо сейчас я могу подумать о том, чтобы передать эту информацию родительскому потоку, используя своего рода передачу сообщений, так что вы можете посмотреть на это.Попробуйте это для размера:
источник
multiprocessing
Эквивалент: gist.github.com/2311116bucket.get()
повышенияQueue.Empty
? Тогда потокjoin(0.1)
завершитсяisAlive() is False
и вы пропустите исключение.Queue
в этом простом случае не требуется - вы можете просто сохранить информацию об исключении в качестве свойства,ExcThread
если только вы убедитесь, что оноrun()
завершается сразу после исключения (что и происходит в этом простом примере). Затем вы просто повторно поднимаете исключение после (или во время)t.join()
. Нет проблем с синхронизацией, потомуjoin()
что убедитесь, что поток завершен. Смотрите ответ Рока Стрниша ниже stackoverflow.com/a/12223550/126362concurrent.futures
Модуль позволяет легко сделать работу в отдельных потоках (или процессов) и обрабатывать любые результирующие исключения:concurrent.futures
входит в состав Python 3.2 и доступен в качествеfutures
модуля обратного переноса для более ранних версий.источник
concurrent.futures.as_completed
, вы можете сразу же получать уведомления приНа этот вопрос есть много действительно сложных ответов. Я упрощаю это, потому что мне кажется, что этого достаточно для большинства вещей.
Если вы уверены, что когда-либо будете работать только на одной или другой версии Python, вы можете сократить
run()
метод до версии с искажениями (если вы будете работать только на версиях Python до 3), или просто чистая версия (если вы будете работать только на версиях Python, начинающихся с 3).Пример использования:
И вы увидите исключение, возникшее в другом потоке, когда вы присоединитесь.
Если вы используете
six
или используете только Python 3, вы можете улучшить информацию трассировки стека, полученную при повторном возникновении исключения. Вместо стека в точке соединения вы можете заключить внутреннее исключение в новое внешнее исключение и получить обе трассировки стека с помощьюили
источник
Хотя невозможно напрямую перехватить исключение, созданное в другом потоке, вот код, который позволяет совершенно прозрачно получить что-то очень близкое к этой функциональности. Ваш дочерний поток должен наследовать
ExThread
класс вместо,threading.Thread
а родительский поток должен вызыватьchild_thread.join_with_exception()
метод вместо того, чтобыchild_thread.join()
ждать, пока поток завершит свою работу.Технические подробности этой реализации: когда дочерний поток генерирует исключение, он передается родительскому элементу через a
Queue
и снова генерируется в родительском потоке. Обратите внимание, что в этом подходе нет ожидания.источник
BaseException
, не такException
ли? Все, что вы делаете, это распространяете исключение из одногоThread
в другое. Прямо сейчас IE, aKeyboardInterrupt
, будет молча игнорироваться, если он будет вызван в фоновом потоке.join_with_exception
зависает на неопределенный срок, если вызывается второй раз на мертвой ветке. Исправление: github.com/fraserharris/threading-extensions/blob/master/…Queue
это необходимо; см. мой комментарий к ответу @ Санты. Вы можете упростить его до чего-то вроде ответа Рока Стрниши ниже stackoverflow.com/a/12223550/126362Если в потоке возникает исключение, лучший способ - повторно вызвать его в потоке вызывающего во время
join
. Вы можете получить информацию об исключении, которое обрабатывается в данный момент, используяsys.exc_info()
функцию. Эта информация может быть просто сохранена как свойство объекта потока, пока неjoin
будет вызвана, после чего она может быть повторно вызвана.Обратите внимание, что a
Queue.Queue
(как предлагается в других ответах) не является необходимым в этом простом случае, когда поток выбрасывает не более 1 исключения и завершается сразу после создания исключения . Мы избегаем условий гонки, просто ожидая завершения потока.Например, расширение
ExcThread
(ниже), переопределениеexcRun
(вместоrun
).Python 2.x:
Python 3.x:
Форма с тремя аргументами для
raise
отсутствует в Python 3, поэтому измените последнюю строку на:источник
concurrent.futures.as_completed
https://docs.python.org/3.7/library/concurrent.futures.html#concurrent.futures.as_completed
Следующее решение:
Queue
Источник:
Возможный вывод:
К сожалению, невозможно уничтожить фьючерсы, чтобы отменить другие, так как один не получается:
concurrent.features
; Python: concurrent.futures Как сделать его отменяемым?threading
: Есть ли способ убить нить?Если вы делаете что-то вроде:
затем
with
ловит его и ждет окончания второго потока, прежде чем продолжить. Следующее ведет себя аналогично:так как
future.result()
повторно вызывает исключение, если оно произошло.Если вы хотите выйти из всего процесса Python, вы можете сойти с рук
os._exit(0)
, но это, вероятно, означает, что вам нужен рефакторинг.Пользовательский класс с идеальной семантикой исключений
Я закончил программировать идеальный интерфейс для себя на: Правильный способ ограничить максимальное количество одновременно работающих потоков? раздел «Пример очереди с обработкой ошибок». Этот класс призван быть как удобным, так и дать вам полный контроль над отправкой и обработкой результатов / ошибок.
Протестировано на Python 3.6.7, Ubuntu 18.04.
источник
Это была неприятная маленькая проблема, и я хотел бы добавить свое решение. Некоторые другие решения, которые я нашел (например, async.io), выглядели многообещающе, но также представляли собой черную коробку. Подход с использованием очереди / события связывает вас с определенной реализацией. Параллельный исходный код фьючерса, однако, составляет всего около 1000 строк, и его легко понять . Это позволило мне легко решить мою проблему: создавать рабочие потоки ad-hoc без особых настроек и иметь возможность перехватывать исключения в основном потоке.
Мое решение использует API параллельных фьючерсов и API потоков. Это позволяет вам создать работника, который дает вам как поток, так и будущее. Таким образом, вы можете присоединиться к потоку, чтобы дождаться результата:
... или вы можете позволить работнику просто отправить обратный вызов, когда закончите:
... или вы можете выполнить цикл, пока событие не завершится:
Вот код:
... и тестовая функция:
источник
Как новичок в Threading, мне потребовалось много времени, чтобы понять, как реализовать код Матеуша Кобоса (см. Выше). Вот уточненная версия, чтобы помочь понять, как ее использовать.
источник
Подобным образом, как у RickardSjogren без Queue, sys и т. Д., Но также без некоторых прослушивателей сигналов: выполняется непосредственно обработчик исключений, который соответствует блоку исключения.
Только self._callback и блок исключений в run () являются дополнительными к нормальной работе с потоками. Тема.
источник
Я знаю, что немного опоздал на вечеринку здесь, но у меня была очень похожая проблема, но она включала использование tkinter в качестве графического интерфейса, и основной цикл лишил возможности использовать любое из решений, которые зависят от .join (). Поэтому я адаптировал решение, данное в РЕДАКТИРЕ исходного вопроса, но сделал его более общим, чтобы его было легче понять другим.
Вот новый класс потока в действии:
Конечно, вы всегда можете обработать исключение другим способом, например, распечатать его или вывести на консоль.
Это позволяет вам использовать класс ExceptionThread точно так же, как и класс Thread, без каких-либо специальных изменений.
источник
Один метод, который мне нравится, основан на модели наблюдателя . Я определяю класс сигнала, который мой поток использует для выдачи исключений слушателям. Он также может быть использован для возврата значений из потоков. Пример:
У меня недостаточно опыта работы с потоками, чтобы утверждать, что это абсолютно безопасный метод. Но это сработало для меня, и мне нравится гибкость.
источник
Использование обнаженных исключений не является хорошей практикой, потому что вы обычно ловите больше, чем ожидаете.
Я хотел бы предложить изменить
except
ТОЛЬКО исключение, которое вы хотели бы обработать. Я не думаю, что его повышение имеет желаемый эффект, потому что когда вы отправляетесь для создания экземпляраTheThread
во внешнемtry
, если оно вызывает исключение, назначение никогда не произойдет.Вместо этого вы можете просто предупредить об этом и двигаться дальше, например:
Затем, когда это исключение будет обнаружено, вы можете обработать его там. Затем, когда внешнее приложение
try
ловит исключениеTheThread
, вы знаете, что оно не будет тем, с которым вы уже работали, и поможет вам изолировать поток процесса.источник
Простой способ перехвата исключения потока и обратной связи с методом вызывающего может быть путем передачи словаря или списка
worker
методу.Пример (передача словаря в рабочий метод):
источник
Wrap Thread с хранилищем исключений.
источник
pygolang предоставляет sync.WorkGroup, который, в частности, распространяет исключение из порожденных рабочих потоков в основной поток. Например:
дает следующее при запуске:
Исходный код из вопроса будет просто:
источник