imap
/imap_unordered
和 map
/map_async
有两个主要的区别:
- 它们消耗你传递给它们的可迭代对象的方式。
- 它们将结果返回给你的方式。
map
通过将可迭代对象转换为列表(假设它不是列表),将其拆分成块,并将这些块发送到 Pool
中的工作进程中来消耗你的可迭代对象。将可迭代对象拆分成块比以一项一次地在进程之间传递每个项目的方式更加高效,特别是当可迭代对象很大时。然而,为了对其进行分块,将可迭代对象转换为列表可能会产生非常高的内存成本,因为整个列表都需要保留在内存中。
imap
不会将你提供的可迭代对象转换为列表,也不会将其拆分成块(默认情况下)。它将一个元素迭代器一次性地迭代过去,并将它们发送到工作进程中的每个元素。这意味着你不会承受将整个可迭代对象转换为列表所带来的内存负荷,但也意味着对于大型可迭代对象,性能会更慢,因为缺少分块。然而,这可以通过传递一个大于默认值 1 的 chunksize
参数来缓解。
imap
/imap_unordered
和 map
/map_async
的另一个主要区别是,在使用 imap
/imap_unordered
时,你可以在工作进程准备好它们的结果时立即开始接收结果,而不必等待它们全部完成。使用 map_async
时,会立即返回一个 AsyncResult
对象,但实际上只有在所有项目都被处理之后,才能从该对象中检索结果,并且此时它将返回与 map
相同的列表(实际上,map
在内部实现为 map_async(...).get()
)。没有一种方法可以获取部分结果;你要么拥有整个结果,要么什么都没有。
imap
和 imap_unordered
都立即返回可迭代对象。使用 imap
时,结果将按照输入可迭代对象的顺序依次产生。使用 imap_unordered
时,结果将在准备就绪时立即产生,而不考虑输入可迭代对象的顺序。因此,假设你有这样一个可迭代对象:
import multiprocessing
import time
def func(x):
time.sleep(x)
return x + 2
if __name__ == "__main__":
p = multiprocessing.Pool()
start = time.time()
for x in p.imap(func, [1,5,3]):
print("{} (Time elapsed: {}s)".format(x, int(time.time() - start)))
这会输出:
3 (Time elapsed: 1s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)
如果你使用p.imap_unordered
而不是p.imap
,你会看到:
3 (Time elapsed: 1s)
5 (Time elapsed: 3s)
7 (Time elapsed: 5s)
如果您使用p.map
或p.map_async().get()
,您将看到:
3 (Time elapsed: 5s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)
因此,使用imap
/imap_unordered
而不是map_async
的主要原因是:
- 如果将可迭代对象转换为列表会耗尽/使用过多内存,则您的可迭代对象足够大。
- 您希望能够在完成所有结果之前开始处理结果。
apply
会将一个任务发送给工作进程,并一直阻塞直到完成。apply_async
会将一个任务发送给工作进程,然后立即返回一个AsyncResult
对象,可以使用该对象等待任务完成并获取结果。apply
的实现方式是通过简单调用apply_async(...).get()
来实现的。 - dano