Python 如何在concurrent.futures的迭代器中确保每个未来的超时?

Python 如何在concurrent.futures的迭代器中确保每个未来的超时?,python,python-3.x,asynchronous,timeout,concurrent.futures,Python,Python 3.x,Asynchronous,Timeout,Concurrent.futures,concurrent.futures的大约超时时间很难理解。在一个简单的例子中,我想通过在一个循环中调用.submit来使用ProcessPoolExecutor。我希望这些Future对象中的每一个都有一个10分钟的相关超时,否则它们将异步完成 我的第一种方法是尝试使用该函数,该函数生成未来对象的迭代器,并且只在完成后生成下一个迭代器as_completed接受一个timeout参数,但文档说明此超时是相对于调用as_completed时的第一个时刻,而不一定是任何未来的对象本身的生存期 例如

concurrent.futures
的大约超时时间很难理解。在一个简单的例子中,我想通过在一个循环中调用
.submit
来使用
ProcessPoolExecutor
。我希望这些
Future
对象中的每一个都有一个10分钟的相关超时,否则它们将异步完成

我的第一种方法是尝试使用该函数,该函数生成未来对象的迭代器,并且只在完成后生成下一个迭代器
as_completed
接受一个
timeout
参数,但文档说明此超时是相对于调用
as_completed
时的第一个时刻,而不一定是任何
未来的
对象本身的生存期

例如,假设
ProcessPoolExecutor
只有3个工作进程,但
Future
对象列表包含10项。在处理前3个项目时,其中7个项目可能处于未处理状态长达10分钟。此后不久,从
as_completed
开始的超时将被触发,从而导致故障,即使每个
Future
可能已经自己满足了10分钟的限制

请注意,同样适用于
as_completed
的限制也适用于此用例,并且
wait
更难用于此用例,因为它支持有限的返回选项

我的下一个想法是对未来列表中的每个
f
(未来)使用and调用
f.result(timeout=600)
。但是,如果不以阻塞方式实际请求结果,就无法真正设置此超时。如果您迭代期货列表并调用
f.result(…)
,此调用将在指定的超时时间内阻塞

另一方面,您也不能将
f.result
as_completed
组合在一起,以一种天真但似乎正确的方式,如

[f.result(timeout=600) for f in as_completed(futures_list)]
。。。因为
as_completed
的迭代在期货完成时以异步方式等待,并且只返回它们在它们完成后调用
.result


考虑到这一点,什么是正确的模式来生成一个
未来
列表,其中每个都有自己的超时,然后异步等待它们完成?

在这种异步上下文中,似乎没有办法提供每个未来的超时。可用的API函数
wait
as_completed
通过在一组
Future
对象中支持跨所有任务的全局超时,而不尝试测量
Future
首次开始处于被处理状态的时间

我选择了一种解决方法,将我的任务列表分成一组块,并在每个块完成时使用
。区块大小设置为与my
ProcessPoolExecutor
配置为使用的工作线程数相同,因此我可以在某种程度上确保完成时的
的“全局”超时作为每个未来超时秘密运行,因为所有任务都会立即被积极处理。缺点是利用率稍低,因为当任务提前完成时,进程池不能自由地抓取下一个未来任务;它必须等待整个下一批任务。对我来说这没问题,但这是我必须选择的
concurrent.futures
的一个重大可用性缺陷

下面是一些示例代码。假设
my_task_list
已包含通过
functools.partial
或其他方式绑定部分或所有必需参数的函数。您可以对此进行修改,以便在元组或dict的单独iterable中提供参数,并根据需要传递到
submit

my_task_list = #... define your list of task functions
num_workers = #... set number of workers
my_timeout = #... define your timeout
with ProcessPoolExecutor(max_workers=num_workers) as pool:
    all_results = []
    for chunk_start in range(0, len(my_task_list), num_workers):
        chunk = my_task_list[chunk_start:chunk_start + num_workers]
        # could extract parameters to pass for this task chunk here.
        futures = [pool.submit(task) for task in chunk]
        all_results += [
            f.result() for f in as_completed(futures, timeout=my_timeout)
        ]
    return all_results

请注意,如果您选择的
num_workers
高于
ProcessPoolExecutor
可用的处理器数量,您将得到比给定块中的处理器更多的任务,并返回到
as_completed
超时不能正确应用于每个任务的运行时的情况,可能会导致相同类型的超时错误,就像在整个任务列表上使用
as_completed
wait
而不使用分块一样。

到目前为止,似乎没有办法实现这一点。作为一种解决方法,我计划将我的工作功能列表分割成与流程池中的工作人员数量相同大小的块。然后,我将仅通过
submit
将它们转换为futures,并在块上调用
as_completed
,在块上我可以保证超时应立即应用于所有futures。不过,这是一个重大的让步,因为concurrent.futures的API不支持可能是第二个最关键的用例,只能单独处理一个未来。