Python`conners.futures`:根据完成顺序迭代期货

时间:2013-04-29 10:31:12

标签: python multithreading future

我想要类似于executor.map的东西,除非我迭代结果,我想根据完成顺序迭代它们,例如首先完成的工作项应首先出现在迭代中,等等。如果序列中的每个工作项尚未完成,迭代将阻止迭代。

我知道如何使用队列自己实现这个,但我想知道是否可以使用futures框架。

(我主要使用基于线程的执行程序,所以我想要一个适用于这些的答案,但一般的答案也会受到欢迎。)

更新:感谢您的回答!您能否解释一下如何as_completedexecutor.map一起使用? executor.map是使用期货时最实用和最简洁的工具,我不愿意手动开始使用Future个对象。

3 个答案:

答案 0 :(得分:33)

与内置executor.map()一样,

map()仅按可迭代的顺序返回结果,因此遗憾的是,您无法使用它来确定完成顺序。 concurrent.futures.as_completed()正是您所寻找的 - 这是一个例子:

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_as_completed()函数,该函数封装上述内容(可能将其添加到子类Executor()),但我认为创建期货实例通过executor.submit()是一种更简单/更清晰的方式(也允许你提供无参数,kwargs)。

答案 1 :(得分:1)

并发期货会根据完成时间返回一个迭代器 - 这听起来就像你正在寻找的那样。

http://docs.python.org/dev/library/concurrent.futures.html#concurrent.futures.as_completed

如果您有任何混淆或困难,请告诉我。亲切的问候, - 大卫

答案 2 :(得分:0)

From python doc

concurrent.futures.as_completed(fs, timeout=None)¶ 
  
    

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

  

您需要了解executor.map()executor.submit()之间的区别。第一个将函数映射到参数向量。它与map非常相似,但是异步启动任务。 submit(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(),使用submitas_completed()

运行任务
  

返回由fs给出的Future实例上的迭代器   期货完成(已完成或被取消)。