2017-06-14 56 views
0

我一直在使用multiprocessing软件包来加速某些地理处理(GIS/arcpy)任务,这些任务是多余的,需要对2000多个相似的几何体进行相同的处理。为什么我们必须显式传递常量到多处理函数中?

分离效果很好,但我的“工作人员”功能相当长且复杂,因为从头到尾的任务本身都很复杂。我很想分开更多的步骤,但是我无法将信息传递给/从worker函数,因为出于某种原因,需要明确传入多处理使用的worker函数。

这意味着我无法在if __name__ == '__main__'正文中定义常量,然后在辅助函数中使用它们。这也意味着我的工作函数参数列表变得非常长 - 这是超级丑陋,因为尝试使用多个参数也需要创建一个帮助器“星号”函数,然后itertools将它们重新解压缩(第二秒回答于this question)。

我在下面创建了一个简单的例子,演示了我在说什么。是否有任何解决方法 - 我应该使用的不同方法 - 或者至少可以解释一下为什么这是它的方式?

注意:我在Windows Server 2008 R2 Enterprise x64上运行此操作。

编辑:我似乎没有把我的问题弄清楚。我并不关心pool.map如何只接受一个参数(尽管它很烦人),而是我不明白为什么在if __name__ == '__main__'以外定义的函数的作用域如果用作多处理函数时不能访问该块内定义的东西 - 除非你明确地将它作为参数传递,这是令人讨厌的。

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

我不明白你为什么需要在'__name__'块中定义它们,而不是在模块级别,这将起作用。 –

+0

实际上,在块中定义事物对我来说也很好。你为什么认为你需要将所有东西都压缩起来? –

+0

因为Python 2.7的'pool.map'只接受一个函数和一个参数(再次,从我链接的问题)。我很困惑为什么事情正在为你工作 - 也许你可以发布一些有效的代码? – HFBrowning

回答

2

的问题是,至少在Windows上(虽然有与* nix的类似警告多的fork风格,太),当您执行脚本,它(极大地简化IT)有效地结束了作为,如果你叫两个空白(壳)与subprocess.Popen()处理,然后让他们执行:只要那些公关的一个

由一个
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__"块永远不会被执行(因为它不是主脚本,所以它被作为模块导入),因此在其中声明的任何内容都不可用于该函数(因为它从未被评估过)。

对于你的函数之外的工作人员,你至少可以通过访问与globals()module通过sys.modules["your_script"]甚至欺骗但这仅适用于被评估的工作人员,所以任何置于if __name__ == "__main__"后卫内不可用,因为它没”甚至有机会。这也是为什么你必须在Windows上使用这个守护进程的原因 - 没有它你会执行你的池创建,以及你嵌入到守卫中的其他代码,一遍又一遍地处理每个产生的进程。

如果您需要在多重处理功能中共享只读数据,只需在脚本的全局名称空间中定义它,而不是在__main__警卫之外,并且所有功能都有权访问它在开始新流程时进行评估),而不管它们是否作为单独的流程运行。

如果你需要改变的数据,那么你需要使用一些可以在不同进程间自我同步的东西 - 为此设计了许多模块,但大部分时间是Python自己的基于pickle的数据报文通信multiprocessing.Manager(和它提供的类型),虽然速度慢,但不够灵活,就足够了。

+0

这是一个很棒的答案@zwer!我没有解释,但是我不想在模块级环境中包含数据的理由是计算起来非常昂贵(3小时),而且我只需要在每次脚本运行时执行一次。然后我以平行的方式制作了2000份报告,并发布了相同的数据。这听起来像将它们分成两个脚本,并且将数据路径用作后者中的常量将是更清晰的方法。但至少我现在理解了程序执行流程 - 这是我根据文档无法理解的。 – HFBrowning

+1

@HFBrowning - 如果需要很长时间来评估您的数据(假设数量很大)并且您的流程需要无限制地访问整个数据集 - 我建议您将它存储在某些内存数据库中(['Redis'] (例如https://redis.io/)),然后让你的进程轮询来自它的数据。如果进程可以以序列化方式使用数据(因此不需要一次设置整个集合),则可以创建一个流式调度程序模式,如[this](https://stackoverflow.com/a/44502827/7553525)或[this](https://stackoverflow.com/a/44415368/7553525),并以受控方式喂养你的员工。 – zwer

+0

我从来没有听说过'Redis' - 它看起来棒极了!感谢您提供更多建议(: – HFBrowning

0

Python » 3.6.1 Documentation: 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) 

Note: The Var Types inside the class have to be pickleable !
Read about what-can-be-pickled-and-unpickled

相关问题