【问题标题】:Python multiprocessing - How to break from a map efficiently?Python 多处理 - 如何有效地打破地图?
【发布时间】:2015-10-24 12:26:22
【问题描述】:

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

为什么多处理的地图表现不同?如何避免不必要的函数调用?

【问题讨论】:

    标签: python python-3.x python-multiprocessing


    【解决方案1】:

    multiprocessing 的 map 行为不同,因为它不像内置 map 那样同步地读写 map 可迭代对象,它会立即将每个迭代拆分为一个单独的进程并连接结果。

    如果您不熟悉并发原理,我将尝试更好地简要解释一下。

    在您使用内置映射的第一个示例中,代码将创建一个可迭代对象,允许您按顺序一次执行worker。它一次执行一个并按顺序执行的事实意味着打印worker call x= 的函数将始终先打印,然后再继续执行到循环内部,该循环将仅打印x 的值。这也意味着当您的循环达到 2 时,您可以退出循环,而无需对 map 或循环体本身进行任何额外的调用。这是一个同步操作,一切都很有礼貌,等待轮到它执行。

    在您的第二个示例中,使用多处理映射代码仍将创建一个处理 worker(x) 的可迭代对象。但是,这一次,您不是一次(同步)执行对worker(x) 的每个调用。多处理映射调用将立即将所有映射调用发送到单独的进程以首先执行,然后组合结果。然后,您的循环执行组合结果,并按照您的指示再次在 2 处停止。不幸的是,所有映射条目都已在单独的进程中执行,因此虽然循环主体执行的次数最少,但映射没有。

    希望这可以帮助您更好地理解原因。

    【讨论】:

      【解决方案2】:

      multiprocessing.Pools 的基本性质是,只要你说pool.map(...),它就会将传递的迭代中的所有任务提交到队列中,以供工作进程执行。一旦这样的任务被放入池中,它最终会被一个工作进程消费并处理。你对结果所做的任何事情都无法改变这一点。

      【讨论】:

        【解决方案3】:

        需要注意的是,如果你用 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 声明:

        将函数应用于每个可迭代项并返回结果列表...

        当 Python 3 doc 声明时:

        返回一个迭代器,它将函数应用于可迭代的每个项目,产生结果...

        这意味着map 在 Python 3 中被更改为返回一个可迭代对象而不是一个列表。

        甚至在 Python 3 中,multiprocessing.pool.Pool.map doc 说:

        map() 内置函数的并行等效项(尽管它仅支持一个可迭代参数)。 它会一直阻塞,直到结果准备好。

        (强调我的)

        这意味着该方法首先通过生成多个进程来计算结果列表,然后才返回一个完整的结果对象,而不是在每次子进程结束时产生一个值。这样一来,它更接近 Python2 map 内置而不是 Python3。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-09-02
          • 2017-02-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-08
          • 1970-01-01
          相关资源
          最近更新 更多