考虑以下玩具示例。我正在进行并行化以计算方形函数的值,同时对共享对象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修改共享对象。提前谢谢!
答案 0 :(得分:2)
如果您的startmethod
为spawn
或forkserver
,则A
首先不是共享对象。如果您使用的是Windows,spawn
是默认设置,也是唯一的选择。
如果您的startmethod
是fork
,那么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)