为什么我们必须将常量显式传递给多处理函数?

时间:2017-06-14 16:50:00

标签: python multiprocessing

我一直在使用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()

2 个答案:

答案 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