[PYTHON] PyInstaller로 빌드 된 Windows EXE가 다중 처리와 함께 실패합니다.
PYTHONPyInstaller로 빌드 된 Windows EXE가 다중 처리와 함께 실패합니다.
내 프로젝트에서는 파이썬 다중 처리 라이브러리를 사용하여 __main__에 여러 프로세스를 작성했습니다. 이 프로젝트는 PyInstaller 2.1.1을 사용하여 단일 Windows EXE에 패키지됩니다.
나는 이렇게 새로운 프로세스를 만든다 :
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'
이 "솔루션"에 대한 나의 좌절감은 패치가 정확히 무엇인지 정확히 알지 못한다. 둘째, 복잡하고 복잡한 방식으로 작성되어 어떤 부분이 솔루션인지, 어떤 부분이 솔루션인지를 판단 할 수 없다는 것이다. 삽화.
누구나이 문제에 관해 의견을 나눌 수 있고 PyInstaller로 빌드 된 단일 파일 Windows 실행 파일에서 멀티 프로세싱을 가능하게하는 프로젝트에서 정확하게 변경해야 할 사항을 파악할 수 있습니까?
해결법
-
==============================
1.nikola의 답변에 추가하려면 ...
nikola의 답변에 추가하려면 ...
* nix (Linux, Mac OS X 등)는 PyInstaller가 작동하기 위해 어떤 변경도 요구하지 않습니다. (여기에는 --onedir 옵션과 --onefile 옵션이 모두 포함됩니다.) * nix 시스템 만 지원하려는 경우, 이것에 대해 걱정할 필요가 없습니다.
그러나 Windows 지원을 계획하고 있다면 --onerir 또는 --onefile과 같이 어떤 옵션을 선택 하느냐에 따라 코드를 추가해야합니다.
--onedir을 사용할 계획이라면 특별한 메서드 호출 만 추가하면됩니다 :
if __name__ == '__main__': # On Windows calling this function is necessary. multiprocessing.freeze_support()
문서에 따르면이 호출은 __name__ == '__main__': 인 경우 즉시 수행되어야합니다. 그렇지 않으면 작동하지 않습니다. (이 두 줄은 메인 모듈에 두는 것이 강력히 권장됩니다.)
그러나 실제로는 호출하기 전에 확인을 할 여유가 있으며 상황은 계속됩니다.
if __name__ == '__main__': if sys.platform.startswith('win'): # On Windows calling this function is necessary. multiprocessing.freeze_support()
그러나 multiprocessing.freeze_support ()를 호출하는 것은 다른 플랫폼 및 상황에서도 가능합니다. 실행은 Windows에서의 고정 지원에만 영향을 미칩니다. 바이트 코드라면, 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 다중 처리 지원을위한 코드에 사소한 오류가 있음에 유의하십시오. os.sep를 _MEIPASS2 환경 변수에 추가합니다. (줄 : 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에서 단일 파일 다중 처리 지원을 활성화합니다 (모든 플랫폼 / 구성에서 안전하며 --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
출처 : PyInstaller Recipe, Python 멀티 프로세싱 문서
-
==============================
2.이 PyInstaller 티켓을 찾은 후에 내 질문에 답하십시오.
이 PyInstaller 티켓을 찾은 후에 내 질문에 답하십시오.
우리가해야 할 일은 아래와 같이 Process (및 _Popen) 클래스를 제공하고 다중 처리 대신 사용하는 것입니다. 프로세스. 나는 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
from https://stackoverflow.com/questions/24944558/pyinstaller-built-windows-exe-fails-with-multiprocessing by cc-by-sa and MIT license
'PYTHON' 카테고리의 다른 글
[PYTHON] 파이썬 - 특정 클래스에 의해 시작된 활성 스레드 수를 얻는 방법? (0) | 2018.11.18 |
---|---|
[PYTHON] Canvas에서 이미지를 업데이트하는 방법은 무엇입니까? (0) | 2018.11.18 |
[PYTHON] Excel에서 셀 너비 조정 (0) | 2018.11.18 |
[PYTHON] 파이썬에서 매트릭스를 희소하게하는 csv (0) | 2018.11.18 |
[PYTHON] 더 강력한 HTML 파서를 파이썬 기계화에 연결할 수 있습니까? (0) | 2018.11.18 |