2014-07-24 4 views
10

के साथ विफल रहता है मेरी प्रोजेक्ट में मैं पाइथन की multiprocessing लाइब्रेरी का उपयोग कर रहा हूं ताकि __main__ में कई प्रक्रियाएं हो सकें। इस परियोजना को PyInstaller 2.1.1 का उपयोग कर एक एकल विंडोज एक्सई में पैक किया जा रहा है।पायइंस्टॉलर-निर्मित विंडोज एक्सई मल्टीप्रोसेसिंग

मैं बहुत की तरह नई प्रक्रियाओं बनाने के लिए:

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 के मुखपृष्ठ पर एक नुस्खा का दावा है कि जब मैं एक फ़ाइल के रूप में एप्लिकेशन को पैकेजिंग करता हूं तो मुझे विंडोज़ में मल्टीप्रोसेसिंग सक्षम करने के लिए अपना कोड संशोधित करना होगा।

मैं यहाँ कोड के पुनरुत्पादन हूँ:

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- निर्मित एकल फ़ाइल विंडोज एक्जिक्यूटिव में मल्टीप्रोसेसिंग को सक्षम बनाता है?

+0

नुस्खा इस मुद्दे को ठीक करता है? – dano

+0

ठीक है, यह अस्पष्ट है (कम से कम मेरे लिए) नुस्खा कैसे लागू करें। मेरे मुख्य पायथन लिपि में उपरोक्त कोड को चिपकाकर काम नहीं करता है, या तो, क्योंकि यह मेरे पायथन स्क्रिप्ट से संबंधित दो और अपवाद उठाता है। जो मुझे बताता है कि नुस्खा मौलिक रूप से त्रुटिपूर्ण है। – nikola

+0

यदि आप सिर्फ एक स्टैंडअलोन स्क्रिप्ट के रूप में नुस्खा चलाते हैं, तो क्या यह त्रुटियों के बिना चलता है? – dano

उत्तर

6

this PyInstaller ticket पाने के बाद अपने ही प्रश्नों का उत्तर देना:

जाहिर है सभी हम क्या करना है जिन्हें आप नीचे एक Process (और _Popen) वर्ग प्रदान करते हैं और multiprocessing.Process के बजाय इसका उपयोग है। मैंने क्लास को केवल विंडोज़ पर काम करने के लिए सही और सरल बनाया है, * 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 
+1

टिकट लिंक अब मान्य नहीं है। इसके लिए वर्तमान दस्तावेज़ यहां हैं: https://github.com/pyinstaller/pyinstaller/wiki/Recipe- मल्टीप्रोसेसिंग – tom10

10

निकोला का जवाब देने के लिए पर जोड़ने के लिए ...

* nix (लिनक्स, मैक ओएस एक्स, आदि) को PyInstaller के काम करने के लिए किसी भी बदलाव की आवश्यकता नहीं है। (इसमें --onedir और --onefile विकल्प दोनों शामिल हैं।) यदि आप केवल * निक्स सिस्टम का समर्थन करना चाहते हैं, तो इनमें से किसी के बारे में चिंता करने की आवश्यकता नहीं है।

हालांकि, यदि आप विंडोज का समर्थन करने की योजना बना रहे हैं, तो आपको चुनने के विकल्प के आधार पर आपको कुछ कोड जोड़ना होगा: --onedir या --onefile

आप --onedir उपयोग करना चाहते हैं, तुम सब जोड़ने की आवश्यकता होगी एक विशेष विधि कॉल है:

if __name__ == '__main__': 
    # On Windows calling this function is necessary. 
    multiprocessing.freeze_support() 

प्रलेखन के अनुसार, इस कॉल तुरंतif __name__ == '__main__': के बाद किसी और किया जाना चाहिए, या यह होगा काम नहीं। (यह दृढ़ता से सुझाव दिया जाता है कि आपके मुख्य मॉड्यूल में इन दो पंक्तियां हैं।)

वास्तविकता में, तथापि, आप कॉल करने से पहले एक जांच करने के लिए खर्च कर सकते हैं, और चीजों को अभी भी काम करेगा:

if __name__ == '__main__': 
    if sys.platform.startswith('win'): 
     # On Windows calling this function is necessary. 
     multiprocessing.freeze_support() 

हालांकि, बुला multiprocessing.freeze_support() रूप में अच्छी तरह से अन्य प्लेटफार्मों और स्थितियों पर संभव है - यह चल रहा है केवल विंडोज़ पर ठंडे समर्थन को प्रभावित करता है। यदि आप एक बाइटकोड अखरोट हैं, तो आप देखेंगे कि अगर कथन कुछ बाइटकोड जोड़ता है, और संभावित बचत को एक कथन का उपयोग करने से नगण्य बनाता है। इसलिए, आपको केवल if __name__ == '__main__': के बाद तुरंत एक सरल multiprocessing.freeze_support() कॉल करना चाहिए।

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' 

मैं

आप --onefile उपयोग करने की योजना है, तो आप को जोड़ने के लिए निकोला का कोड की आवश्यकता होगी मल्टीप्रोसेसिंग रेसिपी के लिए PyInstaller की नई साइट here से कोड मिला। (उन्होंने अपने ट्रैक आधारित साइट को बंद कर दिया है।)

ध्यान दें कि उनके पास --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 

Error when using os.sep in _MEIPASS2

कोड मैं ऊपर प्रदान की ही है, os.sep बिना। os.sep को हटाने से इस समस्या को हल किया जाता है और --onefile कॉन्फ़िगरेशन का उपयोग कर मल्टीप्रोसेसिंग को काम करने की अनुमति मिलती है।

सारांश में:

को सक्षम करने से विंडोज पर --onedir बहु का समर्थन (विंडोज पर --onefile के साथ काम नहीं करता है, लेकिन अन्यथा सभी प्लेटफार्मों पर सुरक्षित/विन्यास):

if __name__ == '__main__': 
    # On Windows calling this function is necessary. 
    multiprocessing.freeze_support() 

पर --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 

सूत्रों का कहना है: PyInstaller Recipe, Python multiprocessing docs

+1

विस्तृत उत्तर के लिए धन्यवाद। जब मैं _-- onefile_ विकल्प का उपयोग करता हूं तो मुझे अपनी मुख्य पायथन विंडो (टीके के साथ) बंद करने के बाद ज़ोंबी धागे के साथ समस्याएं आ रही थीं। आखिरी स्निपेट जहां आपने पॉपन को फिर से परिभाषित किया था, इस मुद्दे को ठीक कर दिया। पायथन> 3.4 के साथ काम करने वाले किसी भी व्यक्ति के लिए, आपको 'multiprocessing.forking' के बजाय 'आयात multiprocessing.popen_spawn_win32 को फोर्किंग' के रूप में उपयोग करने की आवश्यकता है। –

+0

यह मत भूलना कि 'multiprocessing.freeze_support() 'हमेशा' __name__ ==' __main __ '' में पहली पंक्ति होनी चाहिए और इस लाइन से पहले कोई अन्य कोड निष्पादित नहीं किया जाना चाहिए (यानि '__name__ ==' __main__ से पहले ' ')। मेरे पास कुछ आयात थे जो कुछ कोड निष्पादित करते थे, जिसके परिणामस्वरूप 'multiprocessing.freeze_support()' का कोई प्रभाव नहीं पड़ा। – Guido

संबंधित मुद्दे