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

2024-09-30 22:15:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我一直在使用multiprocessing软件包来加速某些地理处理(GIS/arcpy)任务,这些任务是多余的,需要对2000多个类似的几何图形执行相同的操作。在

分拆工作很好,但我的“工人”职能相当长和复杂,因为任务本身从头到尾都很复杂。我想把这些步骤分解得更详细一些,但是在向worker函数传递信息/从worker函数传递信息时遇到了困难,因为出于某种原因,多处理下的worker函数使用的任何东西都需要显式地传入。在

这意味着我不能在if __name__ == '__main__'的主体中定义常量,然后在worker函数中使用它们。这也意味着我的worker函数的参数列表变得非常长,这是非常难看的,因为尝试使用多个参数还需要创建一个helper“star”函数,然后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()

Tags: 函数textnameifmaindirfunctionroot
2条回答

问题是,至少在Windows上(尽管对于*nixfork样式的多处理也有类似的警告),当您执行脚本时,它(为了极大地简化它)实际上就像您用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__"块永远不会被执行(因为它不是主脚本,它是作为模块导入的),因此它内部声明的任何内容都不容易被函数使用(因为它从未被计算)。在

对于你职能之外的员工,你至少可以通过sys.modules["your_script"]或者甚至使用globals()访问你的module进行欺骗,但这只对被评估的员工有效,因此放在if __name__ == "__main__"保护内的任何东西都不可用,因为它甚至没有机会。这也是为什么你必须在Windows上使用这个保护的原因-没有它,你将执行你的池创建,以及你嵌套在这个保护内的其他代码,一次又一次地与每个派生的进程一起执行。在

如果需要在多处理函数中共享只读数据,只需在脚本的全局命名空间中,在__main__保护之外定义它,那么所有函数都可以访问它(因为在启动新进程时,它会被重新计算),而不管它们是否作为单独的进程运行。在

如果您需要改变的数据,那么您需要使用能够在不同进程上进行自我同步的东西——有很多模块是为此设计的,但是大多数时候Python自己的基于pickle的数据报通信multiprocessing.Manager(以及它提供的类型)就足够了,尽管速度慢而且不太灵活。在

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

相关问题 更多 >