Проверьте, что поток python выбрал исключение

У меня есть набор задач, которые нужно выполнить параллельно, но в конце их мне нужно знать, вызвало ли какое-либо из потоков исключение. Мне не нужно обрабатывать исключение напрямую, мне просто нужно знать, не сработал ли один из потоков с исключением, поэтому я могу чисто завершить script

Вот простой пример:

#!/usr/bin/python

from time import sleep
from threading import Thread

def func(a):
    for i in range(0,5):
        print a
        sleep(1)

def func_ex():
    sleep(2)
    raise Exception("Blah")


x = [Thread(target=func, args=("T1",)), Thread(target=func, args=("T2",)), Thread(target=func_ex, args=())]

print "Starting"
for t in x:
    t.start()

print "Joining"
for t in x:
    t.join()


print "End"

Перед "Конец" я хочу выполнить итерацию по потокам, посмотреть, не удалось ли сбой, а затем решить, могу ли я продолжить работу с script, или если мне нужно выйти с этой точки.

Мне не нужно перехватывать исключение или останавливать другие потоки, мне просто нужно знать в конце, если какой-либо сбой.

Ответ 1

К моменту вызова join() в потоке поток стека потоков был размотан, и вся информация об исключениях была потеряна. Таким образом, к сожалению, вам необходимо предоставить свой собственный механизм регистрации исключений; некоторые методы обсуждаются здесь.

Ответ 2

Простым методом для ситуаций, когда вам не нужно обрабатывать исключение, является использование глобального списка и добавление к нему соответствующей информации. Ваш код станет чем-то вроде:

#!/usr/bin/python

from time import sleep
from threading import Thread, current_thread #needed to get thread name or whatever identifying info you need

threadErrors = [] #global list

def func(a):
    for i in range(0,5):
        print a
        sleep(1)

def func_ex():
    global threadErrors #if you intend to change a global variable from within a different scope it has to be declared
    try:
        sleep(2)
        raise Exception("Blah")
    except Exception, e:
        threadErrors.append([repr(e), current_thread.name]) #append a list of info
        raise #re-raise the exception or use sys.exit(1) to let the thread die and free resources 

x = [Thread(target=func, args=("T1",)), Thread(target=func, args=("T2",)), Thread(target=func_ex, args=())]

print "Starting"
for t in x:
    t.start()

print "Joining"
for t in x:
    t.join()

if len(threadErrors) > 0: #check if there are any errors 
    for e in threadErrors:
        print(threadErrors[e][0]+' occurred in thread: '+threadErrors[e][1])
        #do whatever with each error info
else: 
    #there are no errors so do normal clean-up stuff

#do clean-up that should happen in either case here

print "End"

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