2016-09-13 11 views
0

メインガード(if __name__ == '__main__':)を使用せずにスクリプトからモジュールをインポートした場合、モジュールの一部の機能で並列処理を行うと、Windowsでは無限ループになります。新しいプロセスごとに、すべてのソースがロードされます(__name__'__main__'と等しくない)。メインガードがなければ、クラッシュするまで、新しいプロセスのそれぞれで同じ機能を呼び出し、さらに多くのプロセスを呼び出すことになります。それはWindows上の唯一の問題ですが、スクリプトはosxとlinuxでも実行されます。メインガードのない関数から呼び出した場合の比較

私はこれをディスク上の特別なファイルに書き込んで確認し、すでに読み始めているかどうかを確認することができますが、一度に実行される単一のpythonスクリプトに制限されています。メインガードを追加するためのすべての呼び出しコードを変更する単純な解決策は、アクセスできない多くのリポジトリに広がっているため実行できません。したがって、メインガードが使用されている場合は並列化したいが、そうでない場合はシングルスレッド実行にフォールバックしたい。

メインガードがないためにインポートループで呼び出されているかどうかを確認して、シングルスレッド実行にフォールバックできるようにするにはどうすればよいですか?

ここではいくつかのデモコードです:並列コードと

のlib:

from multiprocessing import Pool 


def _noop(x): 
    return x 


def foo(): 
    p = Pool(2) 
    print(p.map(_noop, [1, 2, 3])) 

(ガード付き)グッド輸入:(ガードなし)

from lib import foo 

if __name__ == "__main__": 
    foo() 

悪い輸入:

from lib import foo 

foo() 
012悪い輸入者が何度も何度も、この例外RuntimeErrorで失敗し

p = Pool(2) 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\context.py", line 118, in Pool 
    context=self.get_context()) 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\pool.py", line 168, in __init__ 
    self._repopulate_pool() 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\pool.py", line 233, in _repopulate_pool 
    w.start() 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\process.py", line 105, in start 
    self._popen = self._Popen(self) 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\context.py", line 313, in _Popen 
    return Popen(process_obj) 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\popen_spawn_win32.py", line 34, in __init__ 
    prep_data = spawn.get_preparation_data(process_obj._name) 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 144, in get_preparation_data 
    _check_not_importing_main() 
    File "C:\Users\filip.haglund\AppData\Local\Programs\Python\Python35\lib\multiprocessing\spawn.py", line 137, in _check_not_importing_main 
    is not going to be frozen to produce an executable.''') 
RuntimeError: 
     An attempt has been made to start a new process before the 
     current process has finished its bootstrapping phase. 

     This probably means that you are not using fork to start your 
     child processes and you have forgotten to use the proper idiom 
     in the main module: 

      if __name__ == '__main__': 
       freeze_support() 
       ... 

     The "freeze_support()" line can be omitted if the program 
     is not going to be frozen to produce an executable. 
+0

を実行しようとしたときに、[最小の例]を作成してくださいできることを意味しますhelp/mcve)?プロセスを生成するために使用した戦略/モジュール(['subprocess')(https://docs.python.org/library/subprocess.html)、[ (https://docs.python.org/library/multiprocessing.html)、['os.spawn'](https://docs.python.org/3/library/os.html#os。 spawnl))? – metatoaster

+0

これは 'multiprocessing'です:) –

答えて

1

あなたがmultiprocessingを使用しているので、あなたも、あなたがメインのプロセスや子プロセスだかどうかを検出するためにそれを使用することができます。しかし、これらの機能は文書化されていないため、Pythonのバージョン間で警告なしに変更できる実装の詳細のみです。

各プロセスは、name,_identityおよび_parent_pidを有する。それらのいずれかをチェックして、あなたがメインプロセスにいるかどうかを確認することができます。メインプロセスではname'MainProcess'_identity()_parent_pidNoneとなります。

私の解決策では、multiprocessingを引き続き使用できますが、子プロセスを永久に作成できないように子プロセスを変更するだけです。これはデコレータを使用して子プロセスのno-opにfooを変更しますが、メインプロセスではfooをそのまま返します。これはhttp://stackoverflow.com/(生成された子プロセスは、それが__main__ガード内部で実行されたかのようfoo何も(起こりません。

from multiprocessing import Pool 
from multiprocessing.process import current_process 

def run_in_main_only(func): 
    if current_process().name == "MainProcess": 
     return func 
    else: 
     def noop(*args, **kwargs): 
      pass 
     return noop 

def _noop(_ignored): 
    p = current_process() 
    return p.name, p._identity, p._parent_pid 

@run_in_main_only 
def foo(): 
    with Pool(2) as p: 
     for result in p.map(_noop, [1, 2, 3]): 
      print(result) # prints something like ('SpawnPoolWorker-2', (2,), 10720) 

if __name__ == "__main__": 
    print(_noop(1)) # prints ('MainProcess',(), None) 
関連する問題