首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ThreadPoolExecutor().map与ThreadPoolExecutor().submit有什么不同?

ThreadPoolExecutor().map与ThreadPoolExecutor().submit有什么不同?
EN

Stack Overflow用户
提问于 2013-12-30 19:00:37
回答 3查看 64.2K关注 0票数 84

我只是被我写的一些代码搞糊涂了。我惊讶地发现:

代码语言:javascript
运行
复制
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(f, iterable))

代码语言:javascript
运行
复制
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    results = list(map(lambda x: executor.submit(f, x), iterable))

产生不同的结果。第一个函数生成f返回的类型的列表,第二个函数生成concurrent.futures.Future对象的列表,然后需要用它们的result()方法对这些对象求值,以便获得f返回的值。

我主要担心的是,这意味着executor.map不能利用concurrent.futures.as_completed,这似乎是一种非常方便的方法来评估我对数据库进行的一些长时间调用的结果。

我根本不清楚concurrent.futures.ThreadPoolExecutor对象是如何工作的--天真地说,我更喜欢(有点冗长):

代码语言:javascript
运行
复制
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    result_futures = list(map(lambda x: executor.submit(f, x), iterable))
    results = [f.result() for f in futures.as_completed(result_futures)]

在更简洁的executor.map上,以便利用可能的性能提升。我这样做是错的吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-12-31 07:03:58

问题是您将ThreadPoolExecutor.map的结果转换为列表。如果您不这样做,而是直接迭代结果生成器,结果仍然会以原始顺序产生,但循环会在所有结果准备就绪之前继续。你可以用这个例子来测试一下:

代码语言:javascript
运行
复制
import time
import concurrent.futures

e = concurrent.futures.ThreadPoolExecutor(4)
s = range(10)
for i in e.map(time.sleep, s):
    print(i)

保持顺序的原因可能是因为有时以与映射相同的顺序获得结果是很重要的。而且结果可能不会包装在将来的对象中,因为在某些情况下,如果需要的话,在列表上执行另一个映射以获取所有结果可能需要太长时间。毕竟,在大多数情况下,下一个值很可能在循环处理第一个值之前就准备好了。下面的示例演示了这一点:

代码语言:javascript
运行
复制
import concurrent.futures

executor = concurrent.futures.ThreadPoolExecutor() # Or ProcessPoolExecutor
data = some_huge_list()
results = executor.map(crunch_number, data)
finals = []

for value in results:
    finals.append(do_some_stuff(value))

在本例中,do_some_stuff的运行时间可能比crunch_number长,如果真的是这样,那么在保持map的易用性的同时,它并不会造成很大的性能损失。

另外,由于工作线程(/processes)从列表的开头开始处理,并一直工作到您提交的列表的末尾,所以结果应该按照迭代器已经产生的顺序完成。这意味着在大多数情况下,executor.map是很好的,但在某些情况下,例如,如果您处理值的顺序并不重要,并且您传递给map的函数的运行时间非常不同,那么future.as_completed可能会更快。

票数 48
EN

Stack Overflow用户

发布于 2020-01-08 10:37:25

如果使用concurrent.futures.as_completed,则可以处理每个函数的异常。

代码语言:javascript
运行
复制
import concurrent.futures
iterable = [1,2,3,4,6,7,8,9,10]

def f(x):
    if x == 2:
        raise Exception('x')
    return x

with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    result_futures = list(map(lambda x: executor.submit(f, x), iterable))
    for future in concurrent.futures.as_completed(result_futures):
        try:
            print('resutl is', future.result())
        except Exception as e:
            print('e is', e, type(e))
# resutl is 3
# resutl is 1
# resutl is 4
# e is x <class 'Exception'>
# resutl is 6
# resutl is 7
# resutl is 8
# resutl is 9
# resutl is 10

executor.map中,如果出现异常,整个执行器都会停止。您需要处理worker函数中的异常。

代码语言:javascript
运行
复制
with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor:
    for each in executor.map(f, iterable):
        print(each)
# if there is any exception, executor.map would stop
票数 20
EN

Stack Overflow用户

发布于 2018-11-29 14:52:48

下面是.submit().map()的对比示例。它们都会立即接受作业(submitted|mapped start)。它们需要相同的时间来完成,11秒(最后结果时间-开始)。但是,只要ThreadPoolExecutor maxThreads=2中的任何线程完成(无序的!),.submit()就会给出结果。而.map()则按照提交的顺序给出结果。

代码语言:javascript
运行
复制
import time
import concurrent.futures

def worker(i):
    time.sleep(i)
    return i,time.time()

e = concurrent.futures.ThreadPoolExecutor(2)
arrIn = range(1,7)[::-1]
print arrIn

f = []
print 'start submit',time.time()
for i in arrIn:
    f.append(e.submit(worker,i))
print 'submitted',time.time()
for r in concurrent.futures.as_completed(f):
    print r.result(),time.time()
print

f = []
print 'start map',time.time()
f = e.map(worker,arrIn)
print 'mapped',time.time()
for r in f:
    print r,time.time()    

输出:

代码语言:javascript
运行
复制
[6, 5, 4, 3, 2, 1]
start submit 1543473934.47
submitted 1543473934.47
(5, 1543473939.473743) 1543473939.47
(6, 1543473940.471591) 1543473940.47
(3, 1543473943.473639) 1543473943.47
(4, 1543473943.474192) 1543473943.47
(1, 1543473944.474617) 1543473944.47
(2, 1543473945.477609) 1543473945.48

start map 1543473945.48
mapped 1543473945.48
(6, 1543473951.483908) 1543473951.48
(5, 1543473950.484109) 1543473951.48
(4, 1543473954.48858) 1543473954.49
(3, 1543473954.488384) 1543473954.49
(2, 1543473956.493789) 1543473956.49
(1, 1543473955.493888) 1543473956.49
票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20838162

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档