我一直在使用multiprocessing
软件包来加速一些冗余的地理处理(GIS / arcpy
)任务,需要对2,000多个类似的几何图形执行相同的操作。
分裂效果很好,但是我的工人"功能相当漫长而复杂,因为任务本身从头到尾都是复杂的。我希望能够将这些步骤分开,但是我无法将信息传递给工作函数或者从工作函数传递信息,因为出于某种原因,需要明确地传递多处理使用的工作函数。
这意味着我无法在if __name__ == '__main__'
的主体中定义常量,然后在worker函数中使用它们。这也意味着我的worker函数的参数列表变得非常长 - 这非常难看,因为尝试使用多个参数也需要创建一个帮助器" star"函数,然后itertools
重新将它们重新压缩(在this question上的第二个答案)。
我在下面创建了一个简单的例子来演示我在说什么。有没有相关的解决方法 - 我应该使用不同的方法 - 或者至少可以解释为什么这就是它的方式?
注意:我在Windows Server 2008 R2 Enterprise x64上运行此功能。
修改:我似乎没有明白我的问题。我并不关心pool.map
如何只接受一个参数(尽管它很烦人),而是我不明白为什么在if __name__ == '__main__'
之外定义的函数的范围无法访问该块内定义的内容< em>如果它被用作多处理函数 - 除非你明确地将它作为参数传递,这是令人讨厌的。
import os
import multiprocessing
import itertools
def loop_function(word):
file_name = os.path.join(root_dir, word + '.txt')
with open(file_name, "w") as text_file:
text_file.write(word + " food")
def nonloop_function(word, root_dir): # <------ PROBLEM
file_name = os.path.join(root_dir, word + '.txt')
with open(file_name, "w") as text_file:
text_file.write(word + " food")
def nonloop_star(arg_package):
return nonloop_function(*arg_package)
# Serial version
#
# if __name__ == '__main__':
# root_dir = 'C:\\hbrowning'
# word_list = ['dog', 'cat', 'llama', 'yeti', 'parakeet', 'dolphin']
# for word in word_list:
# loop_function(word)
#
## --------------------------------------------
# Multiprocessing version
if __name__ == '__main__':
root_dir = 'C:\\hbrowning'
word_list = ['dog', 'cat', 'llama', 'yeti', 'parakeet', 'dolphin']
NUM_CORES = 2
pool = multiprocessing.Pool(NUM_CORES, maxtasksperchild=1)
results = pool.map(nonloop_star, itertools.izip(word_list, itertools.repeat(root_dir)),
chunksize=1)
pool.close()
pool.join()
答案 0 :(得分:2)
问题是,至少在Windows上(虽然也有类似的警告,* nix fork
样式的多处理),当你执行你的脚本时,它(大大简化它)有效地最终成为好像你用subprocess.Popen()
调用了两个空白(shell)进程,然后让它们执行:
python -c "from your_script import nonloop_star; nonloop_star(('dog', 'C:\\hbrowning'))"
python -c "from your_script import nonloop_star; nonloop_star(('cat', 'C:\\hbrowning'))"
python -c "from your_script import nonloop_star; nonloop_star(('yeti', 'C:\\hbrowning'))"
python -c "from your_script import nonloop_star; nonloop_star(('parakeet', 'C:\\hbrowning'))"
python -c "from your_script import nonloop_star; nonloop_star(('dolphin', 'C:\\hbrowning'))"
一旦其中一个进程完成上一次调用,就逐个。这意味着您的if __name__ == "__main__"
块永远不会被执行(因为它不是主脚本,它作为模块导入),因此在其中声明的任何内容都不可用于该函数(因为它从未评估过。)
对于您职能以外的工作人员,您至少可以通过module
通过sys.modules["your_script"]
或globals()
访问,但这仅适用于已评估的工作人员,因此放置了任何内容在if __name__ == "__main__"
后卫内没有,因为它甚至没有机会。这也是为什么你必须在Windows上使用这个防护的原因 - 没有它你将执行你的池创建,以及你在防护中嵌套的其他代码,一遍又一遍地生成每个生成的进程。 / p>
如果您需要在多处理函数中共享只读数据,只需在脚本的全局命名空间中定义它,在__main__
保护之外,并且所有函数都可以访问它(因为它无论是否作为单独的进程运行,都会在启动新进程时重新进行评估。
如果您需要更改的数据,那么您需要使用可以在不同进程上同步的内容 - 为此设计了大量模块,但大部分时间Python都拥有基于pickle的数据报通信<{1}}(以及它提供的类型),虽然速度慢且不够灵活,但已足够。
答案 1 :(得分:0)
Python»3.6.1文档:multiprocessing.pool.Pool
map(func, iterable[, chunksize]) A parallel equivalent of the map() built-in function (it supports only one iterable argument though)
没有限制,只有它必须是可迭代的!
试试class Container
,例如:
class WP(object):
def __init__(self, name):
self.root_dir ='C:\\hbrowning'
self.name = name
word_list = [WP('dog'), WP('cat'), WP('llama'), WP('yeti'), WP('parakeet'), WP('dolphin')]
results = pool.map(nonloop_star, word_list, chunksize=1)
注意:
Var Types
内的class
必须为pickleable
!
阅读what-can-be-pickled-and-unpickled