私のプロジェクトでは、Pythonのmultiprocessing
ライブラリを使用して、__ main__に複数のプロセスを作成しています。プロジェクトは、PyInstaller2.1.1を使用して単一のWindowsEXEにパッケージ化されています。
私は次のような新しいプロセスを作成します。
from multiprocessing import Process
from Queue import Empty
def _start():
while True:
try:
command = queue.get_nowait()
# ... and some more code to actually interpret commands
except Empty:
time.sleep(0.015)
def start():
process = Process(target=_start, args=args)
process.start()
return process
そして__main__で:
if __name__ == '__main__':
freeze_support()
start()
残念ながら、アプリケーションをEXEにパッケージ化して起動すると、次の行でWindowsError
5または6(ランダムに見える)が表示されます。
command = queue.get_nowait()
PyInstallerのホームページのレシピによると、アプリケーションを単一のファイルとしてパッケージ化する場合、Windowsでマルチプロセッシングを有効にするにはコードを変更する必要があります。
私はここでコードを再現しています:
import multiprocessing.forking
import os
import sys
class _Popen(multiprocessing.forking.Popen):
def __init__(self, *args, **kw):
if hasattr(sys, 'frozen'):
# We have to set original _MEIPASS2 value from sys._MEIPASS
# to get --onefile mode working.
# Last character is stripped in C-loader. We have to add
# '/' or '\\' at the end.
os.putenv('_MEIPASS2', sys._MEIPASS + os.sep)
try:
super(_Popen, self).__init__(*args, **kw)
finally:
if hasattr(sys, 'frozen'):
# On some platforms (e.g. AIX) 'os.unsetenv()' is not
# available. In those cases we cannot delete the variable
# but only set it to the empty string. The bootloader
# can handle this case.
if hasattr(os, 'unsetenv'):
os.unsetenv('_MEIPASS2')
else:
os.putenv('_MEIPASS2', '')
class Process(multiprocessing.Process):
_Popen = _Popen
class SendeventProcess(Process):
def __init__(self, resultQueue):
self.resultQueue = resultQueue
multiprocessing.Process.__init__(self)
self.start()
def run(self):
print 'SendeventProcess'
self.resultQueue.put((1, 2))
print 'SendeventProcess'
if __name__ == '__main__':
# On Windows calling this function is necessary.
if sys.platform.startswith('win'):
multiprocessing.freeze_support()
print 'main'
resultQueue = multiprocessing.Queue()
SendeventProcess(resultQueue)
print 'main'
この「解決策」に対する私の不満は、1つは、パッチが正確に何であるかが完全に不明であり、2つは、どの部分が解決策であり、どの部分が単なる図。
誰かがこの問題についていくつかの光を共有し、PyInstallerで構築された単一ファイルのWindows実行可能ファイルでマルチプロセッシングを可能にするプロジェクトで正確に何を変更する必要があるかについての洞察を提供できますか?
見つけた後、私自身の質問に答える このPyInstallerチケット :
どうやら私たちがしなければならないのは、以下に示すようにProcess
(および_Popen
)クラスを提供し、multiprocessing.Process
の代わりにそれを使用することだけです。 Windowsでのみ機能するようにクラスを修正および簡略化しました。* ixシステムでは、異なるコードが必要になる場合があります。
完全を期すために、上記の質問からの適合サンプルを次に示します。
import multiprocessing
from Queue import Empty
class _Popen(multiprocessing.forking.Popen):
def __init__(self, *args, **kw):
if hasattr(sys, 'frozen'):
os.putenv('_MEIPASS2', sys._MEIPASS)
try:
super(_Popen, self).__init__(*args, **kw)
finally:
if hasattr(sys, 'frozen'):
os.unsetenv('_MEIPASS2')
class Process(multiprocessing.Process):
_Popen = _Popen
def _start():
while True:
try:
command = queue.get_nowait()
# ... and some more code to actually interpret commands
except Empty:
time.sleep(0.015)
def start():
process = Process(target=_start, args=args)
process.start()
return process
ニコラの答えに追加するには...
* nix(Linux、Mac OS Xなど)は、PyInstallerが機能するために変更を加える必要はありません。 (これには、_--onedir
_オプションと_--onefile
_オプションの両方が含まれます。)* nixシステムのみをサポートする場合は、これについて心配する必要はありません。
ただし、Windowsのサポートを計画している場合は、選択したオプションに応じて、_--onedir
_または_--onefile
_のコードを追加する必要があります。
_--onedir
_を使用する場合、追加する必要があるのは特別なメソッド呼び出しだけです。
_if __name__ == '__main__':
# On Windows calling this function is necessary.
multiprocessing.freeze_support()
_
ドキュメントによると、この呼び出しは_if __name__ == '__main__':
_の後にすぐに行う必要があります。そうしないと機能しません。 (メインモジュールにこれらの2行を含めることを強くお勧めします。)
ただし、実際には、電話をかける前にチェックを行う余裕があり、それでも問題は解決します。
_if __name__ == '__main__':
if sys.platform.startswith('win'):
# On Windows calling this function is necessary.
multiprocessing.freeze_support()
_
ただし、multiprocessing.freeze_support()
を呼び出すことは、他のプラットフォームや状況でも可能です。これを実行すると、Windowsでのフリーズサポートにのみ影響します。バイトコードがおかしい場合は、ifステートメントがバイトコードを追加し、ifステートメントを使用することで節約できる可能性があることに気付くでしょう。したがって、_if __name__ == '__main__':
_の直後に単純なmultiprocessing.freeze_support()
呼び出しを続ける必要があります。
_--onefile
_を使用する場合は、nikolaのコードを追加する必要があります。
_import multiprocessing.forking
import os
import sys
class _Popen(multiprocessing.forking.Popen):
def __init__(self, *args, **kw):
if hasattr(sys, 'frozen'):
# We have to set original _MEIPASS2 value from sys._MEIPASS
# to get --onefile mode working.
os.putenv('_MEIPASS2', sys._MEIPASS)
try:
super(_Popen, self).__init__(*args, **kw)
finally:
if hasattr(sys, 'frozen'):
# On some platforms (e.g. AIX) 'os.unsetenv()' is not
# available. In those cases we cannot delete the variable
# but only set it to the empty string. The bootloader
# can handle this case.
if hasattr(os, 'unsetenv'):
os.unsetenv('_MEIPASS2')
else:
os.putenv('_MEIPASS2', '')
class Process(multiprocessing.Process):
_Popen = _Popen
# ...
if __name__ == '__main__':
# On Windows calling this function is necessary.
multiprocessing.freeze_support()
# Use your new Process class instead of multiprocessing.Process
_
上記を彼の残りのコード、または以下と組み合わせることができます。
_class SendeventProcess(Process):
def __init__(self, resultQueue):
self.resultQueue = resultQueue
multiprocessing.Process.__init__(self)
self.start()
def run(self):
print 'SendeventProcess'
self.resultQueue.put((1, 2))
print 'SendeventProcess'
if __name__ == '__main__':
# On Windows calling this function is necessary.
multiprocessing.freeze_support()
print 'main'
resultQueue = multiprocessing.Queue()
SendeventProcess(resultQueue)
print 'main'
_
マルチプロセッシングレシピ用のPyInstallerの新しいサイトである ここ からコードを取得しました。 (彼らはTracベースのサイトをシャットダウンしたようです。)
_--onefile
_マルチプロセッシングサポートのコードにマイナーエラーがあることに注意してください。 __MEIPASS2
_環境変数にos.sepを追加します。 (行:os.putenv('_MEIPASS2', sys._MEIPASS + os.sep)
)これは物事を壊します:
_ File "<string>", line 1
sys.path.append(r"C:\Users\Albert\AppData\Local\Temp\_MEI14122\")
^
SyntaxError: EOL while scanning string literal
_
上で提供したコードは同じですが、_os.sep
_はありません。 _os.sep
_を削除すると、この問題が修正され、_--onefile
_構成を使用してマルチプロセッシングを機能させることができます。
要約:
Windowsで_--onedir
_マルチプロセッシングサポートを有効にする(Windowsでは_--onefile
_では機能しませんが、それ以外の場合はすべてのプラットフォーム/構成で安全です):
_if __name__ == '__main__':
# On Windows calling this function is necessary.
multiprocessing.freeze_support()
_
Windowsで_--onefile
_マルチプロセッシングサポートを有効にする(すべてのプラットフォーム/構成で安全、_--onedir
_と互換性があります):
_import multiprocessing.forking
import os
import sys
class _Popen(multiprocessing.forking.Popen):
def __init__(self, *args, **kw):
if hasattr(sys, 'frozen'):
# We have to set original _MEIPASS2 value from sys._MEIPASS
# to get --onefile mode working.
os.putenv('_MEIPASS2', sys._MEIPASS)
try:
super(_Popen, self).__init__(*args, **kw)
finally:
if hasattr(sys, 'frozen'):
# On some platforms (e.g. AIX) 'os.unsetenv()' is not
# available. In those cases we cannot delete the variable
# but only set it to the empty string. The bootloader
# can handle this case.
if hasattr(os, 'unsetenv'):
os.unsetenv('_MEIPASS2')
else:
os.putenv('_MEIPASS2', '')
class Process(multiprocessing.Process):
_Popen = _Popen
# ...
if __name__ == '__main__':
# On Windows calling this function is necessary.
multiprocessing.freeze_support()
# Use your new Process class instead of multiprocessing.Process
_