繁体   English   中英

在Python多处理中使用__name __ =='__ main__'的解决方法

[英]Workaround for using __name__=='__main__' in Python multiprocessing

众所周知,我们需要在使用if __name__ == '__main__'在Python中使用multiprocessing运行代码时保护main()

我理解在某些情况下这是必要的,以提供对main中定义的函数的访问,但我不明白为什么在这种情况下这是必要的:

file2.py

import numpy as np
from multiprocessing import Pool
class Something(object):
    def get_image(self):
        return np.random.rand(64,64)

    def mp(self):
        image = self.get_image()
        p = Pool(2)
        res1 = p.apply_async(np.sum, (image,))
        res2 = p.apply_async(np.mean, (image,))
        print(res1.get())
        print(res2.get())
        p.close()
        p.join()

main.py

from file2 import Something
s = Something()
s.mp()

Something工作所需的所有函数或导入都是file2.py一部分。 为什么子main.py需要重新运行main.py

我认为__name__解决方案不是很好,因为这阻止我分发file2.py的代码,因为我不能确保它们保护他们的主要。 Windows没有解决方法吗? 如何解决这个问题(因为我从来没有遇到任何问题,没有用任何软件包来保护我的主程序 - 他们只是不使用多处理?)

编辑:我知道这是因为在Windows中没有实现fork() 我只是问是否有一个hack让解释器从file2.py而不是main.py开始,因为我可以肯定file2.py是自给自足的

使用“spawn”start方法时,新进程是从头开始的Python解释器。 子进程中的新Python解释器无法确定需要导入哪些模块,因此它们会再次导入主模块,而后者又会导入其他所有模块。 这意味着必须可以导入主模块而没有任何副作用。

如果您使用的是与Windows不同的平台,则可以使用“fork”启动方法,而不会出现此问题。

那就是说,使用if __name__ == "__main__":有什么问题if __name__ == "__main__": 它有许多额外的好处,例如文档工具将能够处理您的主模块,单元测试更容易等,所以您应该在任何情况下使用它。

Windows上需要if __name__ == '__main__' ,因为windows没有进程的“fork”选项。

例如,在linux中,您可以fork进程,因此将复制父进程,并且副本将成为子进程(并且它将有权访问您在父进程中加载​​的已导入的代码)

由于你无法在Windows中进行分叉,因此python只是在子进程中导入父进程导入的所有代码。 这会产生类似的效果,但如果您不执行__name__技巧,则此导入将在子进程中再次执行您的代码(这将使其创建自己的子进程,依此类推)。

因此,即使在您的示例中, main.py也会再次导入(因为所有文件都会再次导入)。 python猜不到子进程应该导入什么特定的python脚本。

仅供参考,你应该注意使用全局变量的其他限制,你可以在这里阅读它https://docs.python.org/2/library/multiprocessing.html#windows

主模块已导入(但使用__name__ != '__main__'因为Windows正在尝试在没有分叉的系统上模拟类似分叉的行为)。 multiprocessing无法知道您在主模块中没有做任何重要事情,因此导入是“以防万一”来创建类似于主进程中的环境。 如果它没有这样做,那么在main中出现副作用的各种东西(例如导入,具有持久副作用的配置调用等)可能无法在子进程中正确执行。

因此,如果他们不保护他们的__main__ ,代码不是多处理安全的(也不是unittest安全,导入安全等)。 if __name__ == '__main__':保护包装应该是所有正确主模块的一部分。 继续进行分发,并附上有关要求多处理安全主模块保护的说明。

正如其他人所提到的,Windows上的spawn()方法将为每个解释器实例重新导入代码。 此导入将在子进程中再次执行您的代码(这将使其创建自己的子进程,依此类推)。

解决方法是将多处理脚本拉入单独的文件,然后使用子进程从主脚本启动它。

我通过在临时目录中将变量绑定到脚本中来传递变量,然后使用argparse将临时目录传递到子进程中。

然后我将结果挑选到临时目录中,主脚本在其中检索它们。

这是我写的一个示例file_hasher()函数:

main_program.py

import os, pickle, shutil, subprocess, sys, tempfile

def file_hasher(filenames):
    try:
        subprocess_directory = tempfile.mkdtemp()
        input_arguments_file = os.path.join(subprocess_directory, 'input_arguments.dat')
        with open(input_arguments_file, 'wb') as func_inputs:
            pickle.dump(filenames, func_inputs)
        current_path = os.path.dirname(os.path.realpath(__file__))
        file_hasher = os.path.join(current_path, 'file_hasher.py')
        python_interpreter = sys.executable
        proc = subprocess.call([python_interpreter, file_hasher, subprocess_directory],
                               timeout=60, 
                              )
        output_file = os.path.join(subprocess_directory, 'function_outputs.dat')
        with open(output_file, 'rb') as func_outputs:
            hashlist = pickle.load(func_outputs)
    finally:
        shutil.rmtree(subprocess_directory)
    return hashlist

file_hasher.py

#! /usr/bin/env python
import argparse, hashlib, os, pickle
from multiprocessing import Pool

def file_hasher(input_file):
    with open(input_file, 'rb') as f:
        data = f.read()
        md5_hash = hashlib.md5(data)
    hashval = md5_hash.hexdigest()
    return hashval

if __name__=='__main__':
    argument_parser = argparse.ArgumentParser()
    argument_parser.add_argument('subprocess_directory', type=str)
    subprocess_directory = argument_parser.parse_args().subprocess_directory

    arguments_file = os.path.join(subprocess_directory, 'input_arguments.dat')
    with open(arguments_file, 'rb') as func_inputs:
        filenames = pickle.load(func_inputs)

    hashlist = []
    p = Pool()
    for r in p.imap(file_hasher, filenames):
        hashlist.append(r)

    output_file = os.path.join(subprocess_directory, 'function_outputs.dat')
    with open(output_file, 'wb') as func_outputs:
        pickle.dump(hashlist, func_outputs)

一定会有更好的办法...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM