Я тоже подошел к проблеме, и я очень смущен этим.Это происходит только с ProcessPoolExecutor, а не с ThreadPoolExecutor.
Проблема заключается в том, что ProcessPoolExecutor пытается создать копию исключения посредством создания второго раза.Для этого ProcessPoolExecutor, кажется, проверяет «верхний» класс - например, исключение, за исключением NoPoolBreaker3, где верхним классом является сам NoPoolBreaker3 - на предмет его атрибутов.Поскольку исключение не всегда содержит все атрибуты всего объекта, оно завершается ошибкой.
Вы увидите, что если вы добавите печать в конструктор:
from concurrent.futures.process import ProcessPoolExecutor
class PoolBreaker(Exception):
def __init__(self, num):
super().__init__()
print(f"PoolBreaker Constructor with num={num}")
self.num = num
class NoPoolBreaker1(Exception):
def __init__(self, num=-9):
super().__init__()
print(f"NoPoolBreaker1 Constructor with num={num}")
self.num = num
class NoPoolBreaker2(Exception):
def __init__(self, num):
super().__init__(num)
print(f"NoPoolBreaker2 Constructor with num={num}")
self.num = num
class NoPoolBreaker3(Exception):
def __init__(self, num):
print(f"NoPoolBreaker3 Constructor with num={num}")
self.num = num
def get_result(job):
exc = job.exception()
if type(exc) is PoolBreaker:
print("PoolBreaker", exc.num)
elif type(exc) is NoPoolBreaker1:
print("NoPoolBreaker1", exc.num)
elif type(exc) is NoPoolBreaker2:
print("NoPoolBreaker2", exc.num)
elif type(exc) is NoPoolBreaker3:
print("NoPoolBreaker3", exc.num)
else:
print(f"Exception: {str(exc)}")
def work(x: int):
if x == 0:
raise PoolBreaker(x)
elif x == 1:
raise NoPoolBreaker1(x)
elif x == 2:
raise NoPoolBreaker2(x)
elif x == 3:
raise NoPoolBreaker3(x)
if __name__ == '__main__':
for num in range(0, 4):
with ProcessPoolExecutor() as executor:
job = executor.submit(work, num)
get_result(job)
и ее вывод (python 3.7.3):
PoolBreaker Constructor with num=0
Exception: A process in the process pool was terminated abruptly while the future was running or pending.
NoPoolBreaker1 Constructor with num=1
NoPoolBreaker1 Constructor with num=-9
NoPoolBreaker1 1
NoPoolBreaker2 Constructor with num=2
NoPoolBreaker2 Constructor with num=2
NoPoolBreaker2 2
NoPoolBreaker3 Constructor with num=3
NoPoolBreaker3 Constructor with num=3
NoPoolBreaker3 3
Как видите, конструктор исключений вызывается два раза, но давайте рассмотрим случаи:
- PoolBreaker: Исключение PoolBreaker успешносоздано.После этого библиотека проверяет этот объект Exception и не находит никаких атрибутов.Конструктор вызывается второй раз, но параметры не передаются, поэтому он не работает.Это не сделано в MWE, но если вы напечатаете трассировку стека, вы увидите, что он жалуется на
TypeError: __init__() missing 1 required positional argument: 'num'
- NoPoolBreaker1: он также не находит никаких атрибутов, но когда конструктор вызывается с нулемаргументы, это не ошибка, потому что у него есть значение по умолчанию (вы видите в выводе, что num = -9)
- NoPoolBreaker2: Поскольку super () вызывается с параметром, исключение имеет атрибут
num
.Таким образом, второй вызов конструктора получает требуемый атрибут num
с правильным значением, поэтому он работает. - NoPoolBreaker3: не вызывается super () и проверяется NoPoolBreaker3, и он находит
num
приписывать.Таким образом, второй вызов конструктора получает требуемый атрибут num
с правильным значением, поэтому он работает.
Фактически возвращаемое исключение всегда первое, второе кажется выброшенным.
Для переноса объектов между процессами их нужно протравить и скопировать, что выполняется ProcessPoolExecutor.Это может быть причиной повторного создания исключения.
Конечно, это не решение проблемы, к сожалению, я еще никого не нашел.
Я тем временем думаю, что это может быть ошибкой в библиотеке, потому что:
- Второе создание объекта не предназначено, или
- Проверка атрибутов неверна
Просто замечание: у меня сейчас проблема в том, что в определенной программе NoPoolBreaker3 больше не работает и также вызывает исключение «внезапно завершено».Я понятия не имею, почему, но это, вероятно, означает, что поведение зависит от большего количества вещей, чем показано в MWE.