Python';s`concurrent.futures`:根据完成顺序对futures进行迭代

Python';s`concurrent.futures`:根据完成顺序对futures进行迭代,python,multithreading,future,Python,Multithreading,Future,我想要类似于executor.map的东西,除了在迭代结果时,我想根据完成顺序对结果进行迭代,例如,首先完成的工作项应该首先出现在迭代中,这样迭代将阻止序列中的每个工作项尚未完成 我知道如何使用队列实现这一点,但我想知道是否可以使用futures框架 (我主要使用基于线程的执行器,所以我希望得到一个适用于这些执行器的答案,但也欢迎给出一个通用答案。) 更新:感谢您的回答!你能解释一下我是如何在executor.map中使用的吗executor.map是我使用futures时最有用、最简洁的工具,

我想要类似于
executor.map
的东西,除了在迭代结果时,我想根据完成顺序对结果进行迭代,例如,首先完成的工作项应该首先出现在迭代中,这样迭代将阻止序列中的每个工作项尚未完成

我知道如何使用队列实现这一点,但我想知道是否可以使用
futures
框架

(我主要使用基于线程的执行器,所以我希望得到一个适用于这些执行器的答案,但也欢迎给出一个通用答案。)


更新:感谢您的回答!你能解释一下我是如何在
executor.map
中使用
的吗
executor.map是我使用futures时最有用、最简洁的工具,我不愿意手动开始使用Future
对象。

并发futures根据完成时间返回一个迭代器——这听起来正是您要找的

请让我知道,如果你有任何困惑或困难wrt的实施。亲切的问候,-David

返回一个迭代器 未来的实例(可能由不同的执行者创建) 实例)由fs给出,在完成(完成)时产生期货 或被取消)。之前完成的任何期货,如_completed() 被称为“将首先屈服”。返回的迭代器引发 如果调用了下一步(),但结果不可用,则TimeoutError 从原始调用as_completed()到超时秒后。 超时可以是int或float。如果未指定超时或无超时, 等待时间没有限制

您需要了解
executor.map()
executor.submit()
之间的区别。第一种方法将函数映射到参数向量。它非常类似于
map
,但是异步启动任务<代码>提交(func,arg)
每次调用启动一个任务。在此任务中,
func
应用于
arg

下面是一个使用
as_completed()
submit()
的示例,我可以在python 3.0上运行它

from concurrent import futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

def load_url(url, timeout):
    return urllib.request.urlopen(url, timeout=timeout).read()

def main():
    with futures.ThreadPoolExecutor(max_workers=5) as executor:
        future_to_url = dict(
            (executor.submit(load_url, url, 60), url)
             for url in URLS)

        for future in futures.as_completed(future_to_url):
            url = future_to_url[future]
            try:
                print('%r page is %d bytes' % (
                          url, len(future.result())))
            except Exception as e:
                print('%r generated an exception: %s' % (
                          url, e))

if __name__ == '__main__':
    main()
此处不使用
map()
,任务通过
submit
as_completed()运行

返回fs给定的未来实例的迭代器,该迭代器生成 期货完成时(完成或取消)

,与内置函数一样,只按iterable的顺序返回结果,因此很遗憾,您无法使用它来确定完成顺序。这就是您想要的-以下是一个示例:

import time
import concurrent.futures

times = [3, 1, 2]

def sleeper(secs):
    time.sleep(secs)
    print('I slept for {} seconds'.format(secs))
    return secs

# returns in the order given
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    print(list(executor.map(sleeper, times)))

# I slept for 1 seconds
# I slept for 2 seconds
# I slept for 3 seconds
# [3, 1, 2]

# returns in the order completed
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    futs = [executor.submit(sleeper, secs) for secs in times]
    print([fut.result() for fut in concurrent.futures.as_completed(futs)])

# I slept for 1 seconds
# I slept for 2 seconds
# I slept for 3 seconds
# [1, 2, 3]
当然,如果需要使用映射接口,您可以创建自己的
map\u as\u completed()
函数来封装上述内容(可能将其添加到子类
Executor()
),但我认为通过
Executor.submit()
创建未来实例是一种更简单、更干净的方法(还允许您不提供args、kwargs)

import time
import concurrent.futures

times = [3, 1, 2]

def sleeper(secs):
    time.sleep(secs)
    print('I slept for {} seconds'.format(secs))
    return secs

# returns in the order given
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    print(list(executor.map(sleeper, times)))

# I slept for 1 seconds
# I slept for 2 seconds
# I slept for 3 seconds
# [3, 1, 2]

# returns in the order completed
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    futs = [executor.submit(sleeper, secs) for secs in times]
    print([fut.result() for fut in concurrent.futures.as_completed(futs)])

# I slept for 1 seconds
# I slept for 2 seconds
# I slept for 3 seconds
# [1, 2, 3]