我想要类似于executor.map
的东西,除非我迭代结果,我想根据完成顺序迭代它们,例如首先完成的工作项应首先出现在迭代中,等等。如果序列中的每个工作项尚未完成,迭代将阻止迭代。
我知道如何使用队列自己实现这个,但我想知道是否可以使用futures
框架。
(我主要使用基于线程的执行程序,所以我想要一个适用于这些的答案,但一般的答案也会受到欢迎。)
更新:感谢您的回答!您能否解释一下如何as_completed
与executor.map
一起使用? executor.map
是使用期货时最实用和最简洁的工具,我不愿意手动开始使用Future
个对象。
答案 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)
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()
,使用submit
和as_completed()
返回由fs给出的Future实例上的迭代器 期货完成(已完成或被取消)。