map()和pool.map()的区别
我有一段代码如下:
def plotFrame(n):
a = data[n, :]
do_something_with(a)
data = loadtxt(filename)
ids = data[:,0] # some numbers from the first column of data
map(plotFrame, ids)
这段代码对我来说运行得很好。现在我想试着把 map()
替换成 pool.map()
,代码如下:
pools = multiprocessing.Pool(processes=1)
pools.map(plotFrame, ids)
但是这样做不行,报错信息是:
NameError: global name 'data' is not defined
我的问题是:这是怎么回事?为什么 map()
对于没有传给函数的 data
变量没有报错,而 pool.map()
却报错了呢?
补充:我使用的是Linux系统。
补充 2:根据@Bill的第二个建议,我现在有了以下代码:
def plotFrame_v2(line):
plot_with(line)
if __name__ == "__main__":
ff = np.loadtxt(filename)
m = int( max(ff[:,-1]) ) # max id
l = ff.shape[0]
nfig = 0
pool = Pool(processes=1)
for i in range(0, l/m, 50):
data = ff[i*m:(i+1)*m, :] # data of one frame contains several ids
pool.map(plotFrame_v2, data)
nfig += 1
plt.savefig("figs_bot/%.3d.png"%nfig)
plt.clf()
这段代码运行得正如我所期待的那样。然而,现在我又遇到了一个意外的问题:生成的图形是空白的,而之前用 map()
的代码生成的图形是有内容的。
2 个回答
使用 multiprocessing.pool
,你可以创建多个独立的进程来处理共享的(全局的)资源 data
。通常情况下,你可以通过将这个资源明确地设置为 global
,让这些进程在父进程中使用这个共享资源。不过,更好的做法是把所有需要的资源作为函数参数明确地传递给子进程。如果你在Windows系统上工作,这种做法是必须的。你可以查看 这里的多进程指南。
所以你可以尝试这样做:
data = loadtxt(filename)
def plotFrame(n):
global data
a = data[n, :]
do_something_with(a)
ids = data[:,0] # some numbers from the first column of data
pools = multiprocessing.Pool(processes=1)
pools.map(plotFrame, ids)
或者更好的方法是查看 这个讨论,了解如何用 multiprocessing.pool
向函数传递多个参数。一个简单的方法可以是:
def plotFrameWrapper(args):
return plotFrame(*args)
def plotFrame(n, data):
a = data[n, :]
do_something_with(a)
if __name__ == "__main__":
from multiprocessing import Pool
data = loadtxt(filename)
pools = Pool(1)
ids = data[:,0]
pools.map(plotFrameWrapper, zip([data]*len(inds), inds))
print results
最后一点:看起来你在例子中做的唯一事情就是切片数组,你可以先切片,然后把切好的数组传递给你的函数:
def plotFrame(sliced_data):
do_something_with(sliced_data)
if __name__ == "__main__":
from multiprocessing import Pool
data = loadtxt(filename)
pools = Pool(1)
ids = data[:,0]
pools.map(plotFrame, data[ids])
print results
为了避免出现“意外”的问题,尽量不要使用全局变量。
要用内置的 map
函数重现你第一个代码示例中调用 plotFrame
的部分:
def plotFrame(n):
a = data[n, :]
do_something_with(a)
使用 multiprocessing.Pool.map
时,首先要处理全局变量 data
。如果 do_something_with(a)
也使用了一些全局数据,那么它也需要进行相应的修改。
如果你想知道如何将一个 numpy 数组传递给子进程,可以查看这个链接:在共享内存中使用 numpy 数组进行多进程处理。如果你不需要修改这个数组,那就更简单了:
import numpy as np
def init(data_): # inherit data
global data #NOTE: no other globals in the program
data = data_
def main():
data = np.loadtxt(filename)
ids = data[:,0] # some numbers from the first column of data
pool = Pool(initializer=init, initargs=[data])
pool.map(plotFrame, ids)
if __name__=="__main__":
main()
所有的参数要么明确地作为参数传递给 plotFrame
,要么通过 init()
进行继承。
你的第二个代码示例又试图操作全局数据(通过 plt
的调用):
import matplotlib.pyplot as plt
#XXX BROKEN, DO NOT USE
pool.map(plotFrame_v2, data)
nfig += 1
plt.savefig("figs_bot/%.3d.png"%nfig)
plt.clf()
除非你在主进程中绘制一些东西,否则这段代码会保存空白的图形。要么在子进程中绘图,要么将要绘制的数据明确地发送回父进程,比如通过从 plotFrame
返回数据,并使用 pool.map()
的返回值。这里有一个代码示例:如何在子进程中绘图。