Pool()。map:修改共享对象

时间:2018-05-31 23:02:17

标签: python parallel-processing python-multiprocessing

考虑以下玩具示例。我正在进行并行化以计算方形函数的值,同时对共享对象A进行一些修改。

import multiprocessing

A = [1, 2]

def square(i):

    A[i] = 2 + A[i]

    return i * i

square(0)
square(1)

print(A)

A = [1, 2]

multiprocessing.Pool().map(square, [0, 1])

print(A)

输出如下

[3, 4]
[1, 2]

但我希望它是

[3, 4]
[3, 4]

如上所述,方形函数的串行版本设法将A从[1,2]更改为[3,4]。但是pool.map无法修改A.所以我问如何使用pool()。map修改共享对象。提前谢谢!

1 个答案:

答案 0 :(得分:2)

如果您的startmethodspawnforkserver,则A首先不是共享对象。如果您使用的是Windows,spawn是默认设置,也是唯一的选择。

如果您的startmethodfork,那么A可能是共享对象 - 但如果是,则在没有任何锁定的情况下改变它实际上是不安全的。

正如Sharing state between processes中所解释的那样,你应该尽可能地努力不需要共享对象 - 这是多处理的整个过程,即流程彼此隔离 - 但如果你确实需要它们,你必须做一些更复杂的事情。

第一个选项是使用共享内存。在这种情况下,您将列表用作固定大小的小数组,您可以使用Array('i', [1, 2])进行模拟,您可以使用与文档示例完全相同的方法。对于更复杂的情况,您通常需要添加Lock或其他同步机制来保护共享内存。这非常有效且简单,但只有当您的共享数据可以映射到像这样的低级类型时,它才有效。

第二个选项是使用Manager.list([1, 2]),您可以使用与文档中下一个示例完全相同的内容。这样效率要低得多 - 它的工作原理是创建一个队列并来回传递消息,告诉主进程在您想要访问或改变列表时执行工作 - 但它具有易于使用的优点。

但同样,通常最好不要做其中任何一件事,而是首先将代码重写为不需要共享数据。通常这意味着从池任务返回更多数据,然后让主进程以某种方式组合返回的值。当然,如果例如其他任务本身需要查看变异值,则这是棘手的。 (在这种情况下,你经常需要构建Manager正在做的80%,此时你也可以只使用Manager ...)。但在你的玩具示例中,情况并非如此。 (事实上​​,当你认为这是不可避免的必要时,通常意味着你没有想过非确定性会如何影响你的算法,而且无论如何都不会有效......)

以下是您如何解决玩具问题的示例:

import multiprocessing

def square(i, aval):
    # actual return value, i, and value to set A[i] to
    return i*i, i, 2+aval

A = [1, 2]
# pass each A[i] into the function
for result, i, aval in multiprocessing.Pool().starmap(square, zip([0, 1], A)):
    # get the new A[i] out of the function and store it
    A[i] = aval    
print(A)