![](/img/trans.png)
[英]python multiprocessing on windows, if __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()
函數:
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
#! /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.