突破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
为什么多处理的地图表现不同?如何避免不必要的函数调用?
答案 0 :(得分:2)
多处理的映射行为不同,因为它不像内置映射那样同步地对映射可迭代对象进行识别,它会立即将每个迭代拆分为一个单独的进程并加入结果。
如果您不熟悉并发原则,我会尝试简要地解释一下这一点。
在使用内置映射的第一个示例中,代码将创建一个可迭代对象,允许您按顺序逐个执行worker
。它一次执行一个并按顺序执行的事实意味着打印worker call x=
的函数将始终先打印,然后执行继续到循环内部,这将只打印x
的值。这也意味着当你的循环命中2时你可以退出循环而不需要对map或循环体本身进行任何额外的调用。这是一个同步操作,一切都很有礼貌,等待轮到你执行。
在第二个示例中,使用多处理映射代码仍会创建一个处理worker(x)
的可迭代对象。但是,这一次,您不是每次执行worker(x)
每次调用(同步)。多处理映射调用将立即将所有映射调用发送到单独的进程以先执行,然后组合结果。然后你的循环执行组合结果,并在你指示它时再次停在2。不幸的是,所有的映射条目都已经在不同的进程中执行,所以当循环体执行的次数最少时,映射不是。
希望这有助于您了解更好的原因。
答案 1 :(得分:1)
multiprocessing.Pool
的基本性质是,只要你说pool.map(...)
,它就会将传递的iterable中的所有任务提交给队列以供工作进程执行。一旦将这样的任务放入池中,它最终将被工作进程使用并进行处理。你对结果没有任何改变可以改变这一点。
答案 2 :(得分:1)
应该注意的是,如果您尝试使用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 doc声明:
返回一个迭代器,它将函数应用于每个iterable项,产生结果......
这意味着在Python 3中更改了map
以返回可迭代而不是列表。
即使在Python 3中,multiprocessing.pool.Pool.map
doc说:
map()内置函数的并行等价物(它只支持一个可迭代的参数)。 阻止,直到结果准备就绪。
(强调我的)
这意味着该方法首先通过生成多个进程来计算结果列表,然后只返回一个完整的结果对象,而不是每次子进程结束时都产生一个值。通过这种方式,它更接近Python2 map
内置而不是Python3。