簡體   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