脱离map
工作正常:
def worker(x):
print("worker call x=%s" % x)
return x
for x in map(worker, range(5)):
print(x)
if x == 2:
break
worker call x=0
0
worker call x=1
1
worker call x=2
2
但是如果我对multiprocessing
做同样的事情,我得到这个:
from multiprocessing import Pool
pool = Pool(2)
for x in pool.map(worker, range(5)):
print(x)
if x == 2:
break
pool.close()
pool.join()
0
1
2
worker call x=0
worker call x=1
worker call x=2
worker call x=3
worker call x=4
为什么多进程的映射行为不同?如何避免不必要的函数调用?
multiprocessing的map的行为不同,因为它不像内置map那样同步地读取map可迭代对象,它将每次迭代立即拆分为一个单独的进程,并将结果连接在一起。
如果你不熟悉并发原则,我将试着简单地解释一下。
在使用内置映射的第一个示例中,代码将创建一个可迭代对象,允许您按顺序一次执行一个worker
。它一次执行一个并按顺序执行的事实意味着打印worker call x=
的函数将始终先打印,然后再继续执行到循环内部,循环将只打印x
的值。这也意味着当你的循环达到2时,你可以退出循环,而不需要额外调用map或循环体本身。这是一个同步操作,一切都很礼貌,等待轮到它执行。
在第二个示例中,使用多处理映射代码仍然会创建一个处理worker(x)
的可迭代对象。然而,这一次,您没有一次执行对worker(x)
的每个调用(同步)。多处理映射调用将立即将所有映射调用发送给单独的进程,让它们先执行,然后再组合结果。然后循环执行合并后的结果,并按照您的指示再次在2处停止。不幸的是,所有的map条目都已经在不同的进程中执行了,所以虽然循环体执行了最少的次数,但map并没有执行。
希望这能帮助你更好地理解为什么
multiprocessing.Pool
s的基本性质是,只要您输入pool.map(...)
,它就会将传递的可迭代对象中的所有任务提交给队列,供工作进程执行。一旦将这样的任务放入池中,它最终将由工作进程使用并进行处理。你对结果做什么都改变不了这一点。
应该注意的是,如果您使用Python2尝试了第一个版本。x(我做了),结果将是:
worker call x=0
worker call x=1
worker call x=2
worker call x=3
worker call x=4
0
1
2
不涉及任何多进程。
区别在于Python 2中,doc声明:
将函数应用于iterable的每一项,并返回结果列表…
当Python 3文档声明:
返回一个迭代器,对iterable的每一项应用function,产生结果…
这意味着map
在Python 3中被更改为返回可迭代对象而不是列表。
即使在Python 3中,multiprocessing.pool.Pool.map
doc也说:
map()内置函数的并行等效函数(但它只支持一个可迭代参数)。阻塞直到结果准备好。
(强调我的)
这意味着该方法首先通过生成多个进程来计算结果列表,然后才返回完整的结果对象,而不是在每次子进程结束时生成一个值。这样,它更接近Python2 map
内置,而不是Python3。