concurrent.futures.ThreadPoolExecutor吞咽异常(Python 3.6)

问题描述 投票:0回答:3

我尝试在 Windows 7 上的 Python 3.6 中使用

ThreadPoolExecutor
,似乎异常被默默地忽略或停止程序执行。示例代码:

#!/usr/bin/env python3

from time import sleep

from concurrent.futures import ThreadPoolExecutor

EXECUTOR = ThreadPoolExecutor(2)


def run_jobs():
    EXECUTOR.submit(some_long_task1)
    EXECUTOR.submit(some_long_task2, 'hello', 123)
    return 'Two jobs was launched in background!'


def some_long_task1():
    print("Task #1 started!")
    for i in range(10000000):
        j = i + 1
    1/0
    print("Task #1 is done!")


def some_long_task2(arg1, arg2):
    print("Task #2 started with args: %s %s!" % (arg1, arg2))
    for i in range(10000000):
        j = i + 1
    print("Task #2 is done!")


if __name__ == '__main__':
    run_jobs()
    while True:
        sleep(1)

输出:

Task #1 started!
Task #2 started with args: hello 123!
Task #2 is done!

它一直挂在那里,直到我用 Ctrl+C 杀死它。

但是,当我从

1/0
中删除
some_long_task1
时,任务 #1 就毫无问题地完成了:

Task #1 started!
Task #2 started with args: hello 123!
Task #1 is done!
Task #2 is done!

我需要捕获在

ThreadPoolExecutor
不知怎的中运行的函数中引发的异常。

Python 3.6 (Minconda)、Windows 7 x64。

python python-3.x windows-7-x64 python-multithreading cpython
3个回答
36
投票
一旦计算结果可用,

ThreadPoolExecutor.submit
就会返回一个代表计算结果的future对象。为了不忽略提交函数引发的异常,您需要实际访问此结果。首先,您可以更改
run_job
以返回创建的 future:

def run_jobs():
    fut1 = EXECUTOR.submit(some_long_task1)
    fut2 = EXECUTOR.submit(some_long_task2, 'hello', 123)
    return fut1, fut2

然后,让顶级代码wait等待期货完成,并访问它们的结果:

import concurrent.futures

if __name__ == '__main__':
    futures = run_jobs()
    concurrent.futures.wait(futures)
    for fut in futures:
        print(fut.result())

在执行引发异常的 future 上调用

result()
会将异常传播给调用者。在这种情况下,
ZeroDivisionError
将在顶层升高。


2
投票

您可以使用

try
语句来处理异常。这就是您的
some_long_task1
方法的样子:

def some_long_task1():
    print("Task #1 started!")
    try:
        for i in range(10000000):
            j = i + 1
        1/0
    except Exception as exc:
        print('some_long_task1 generated an exception: {}'.format(exc))
    print("Task #1 is done!")

在脚本中使用该方法时的输出:

Task #1 started!
Task #2 started with args: hello 123!
some_long_task1 generated an exception: integer division or modulo by zero
Task #1 is done!
Task #2 is done!
(the last while loop running...)

1
投票

如前面的答案所示,有两种方法可以捕获

ThreadPoolExecutor
的异常 - 检查
future
中的异常或记录它们。这完全取决于人们想要如何处理潜在的异常。一般来说,我的经验法则包括:

  1. 如果我想记录错误,包括跟踪堆栈。
    log.exception
    是更好的选择。它需要添加额外的逻辑来通过
    future.exception()
    记录相同数量的信息。
  2. 如果代码需要在主线程中对不同的异常进行不同的处理。检查
    future
    的状态是正确的方法。另外,您可能会发现函数 as_completed 在这种情况下也很有用。
© www.soinside.com 2019 - 2024. All rights reserved.