2014-07-24 45 views
10

ile başarısız oluyor. Projemde, __main__'de birden çok işlem oluşturmak için Python'un multiprocessing kitaplığını kullanıyorum. Proje, PyInstaller 2.1.1 kullanılarak tek bir Windows EXE'de paketlenmiştir.PyInstaller yerleşik Windows EXE, çok işlemcili

Öyle gibi yeni süreçler oluşturun:

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 

Ve __main__ içinde

: bir EXE içinde uygulamayı ambalaj ve onu başlatırken

if __name__ == '__main__': 
    freeze_support() 

    start() 

yazık ki, ben WindowsError 5 veya 6 olsun (görünüyor rastgele) bu satırda:

command = queue.get_nowait() 

PyInstaller'in ana sayfasındaki bir reçete, Uygulamayı tek bir dosya olarak paketlerken, Windows'da çoklu işlemeyi etkinleştirmek için kodumu değiştirmem gerekiyor.

Burada kod üreten ediyorum:

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' 

bu "çözüm" Benim hayal kırıklığı bir, o böyle bir yazıldığı için o ikincisi de, yama tam olarak ne kesinlikle belirsiz ve, yani kıvrık yol, hangi parçaların çözüm olduğunu ve sadece bir örnek olduğunu ortaya çıkarmanın imkânsız hale gelmesine neden olur.

Herkes bu konuda biraz ışık tutabilir ve PyInstaller tarafından oluşturulmuş tek dosya Windows yürütülebilir dosyalarında çok işlemcili bir projede tam olarak neyin değiştirilmesi gerektiğini anlamak için bilgi sağlayabilir mi?

+0

Python multiprocessing docs tarifi sorunu çözer mi? – dano

+0

Tarifenin nasıl uygulanacağı belli değil (en azından benim için). Python komut dosyamda ilgisiz iki özel durum daha ortaya çıkardığından, yukarıdaki kodu ana Python betiğimde yapıştırmak işe yaramıyor. Hangi tarifin temelde hatalı olduğunu söyledi. – nikola

+0

Tarifeyi sadece bağımsız bir komut dosyası olarak çalıştırırsanız, hatasız çalışır mı? – dano

cevap

6

this PyInstaller ticket bulduktan sonra kendi sorularını yanıtlayan:

Anlaşılan biz yapmamız gereken aşağıda gösterildiği gibi bir Process (ve _Popen) sınıfı sağlamak ve multiprocessing.Process yerine bunu kullanmak. Sadece Windows üzerinde çalışacak sınıfı düzelttim ve basitleştirdim, * ix sistemleri farklı kodlara ihtiyaç duyabilir. ...

* Nix (Linux, Mac OS X, vb

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

Bilet bağlantısı artık geçerli değil. Bunun için mevcut dokümanlar: https://github.com/pyinstaller/pyinstaller/wiki/Recipe-Multiprocessing – tom10

10

Nikola'nın cevabı üzerine eklemek için: Bütünlük adına

, burada yukarıdaki soruya uyarlanan örnek var) PyInstaller'ın çalışması için herhangi bir değişiklik gerektirmez. (Bu, hem --onedir hem de --onefile seçeneklerini içerir.) Yalnızca * nix sistemlerini desteklemeyi düşünüyorsanız, bunlardan endişe etmenize gerek yoktur. Ancak, eğer Windows'ı desteklemeyi planlıyorsanız, hangi seçeneği seçerseniz seçin: --onedir veya --onefile. Eğer --onedir kullanmayı planlıyorsanız

, eklemek gereken tüm özel yöntem çağrısıdır: Belgelere göre

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

bu çağrı aksi takdirde olacak if __name__ == '__main__': hemen sonragerçekleştirmiş veya olmalıdır çalışmıyor (Ana modülünüzde bu iki satıra sahip olmanız şiddetle tavsiye edilir.)

Gerçekte, ancak, aramadan önce bir çek yapabilme gücün ve işler çalışmaya devam eder:

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

Ancak multiprocessing.freeze_support() diğer platformlar ve durumlara mümkündür çağıran - çalıştırmayı sadece Windows'ta donma desteğini etkiler. Bir bytecode somunuysa, if ifadesinin bazı bytecode eklediğini ve if ifadesinin ihmal edilebilir bir şekilde kullanılmasından potansiyel tasarruf sağladığını fark edeceksiniz. Bu nedenle, if __name__ == '__main__':'dan hemen sonra basit bir multiprocessing.freeze_support() aramasına dokunmanız gerekir.

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 

Onun kod kalanı ile yukarıda birleştirmek veya aşağıdaki edebilirsiniz:

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' 

Sana --onefile kullanmayı planlıyorsanız

, eklemek Nikola'nın kod gerekir PyInstaller'in çok işlemli tarifi için yeni sitesi olan here'dan kodu aldı. (Trac tabanlı sitelerini kapattıkları görülüyor.)

--onefile çoklu işlem desteği için kodlarında küçük bir hata olduğunu unutmayın. _MEIPASS2 ortam değişkenine os.sep eklediler. (Hat: os.putenv('_MEIPASS2', sys._MEIPASS + os.sep)) Bu işleri keser:

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

Yukarıda verdiğim kod os.sep olmadan aynıdır. os.sep'un kaldırılması bu sorunu giderir ve çok işlemcinin --onefile yapılandırmasını kullanarak çalışmasına izin verir. Özetle

:

Windows --onedir çoklu işlem desteği etkinleştirme (tüm platformlarda aksi güvenli Windows üzerinde --onefile ile değil iş, ama/yapılandırmaları):

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

tarihinde --onefile çoklu işlem desteği etkinleştirme Windows (tüm platformlar/yapılandırmalarda güvenli, uyumlu --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 

Kaynaklar: PyInstaller Recipe,

+1

Ayrıntılı cevap için teşekkürler. _-Onefile_ seçeneğini kullandığımda ana Python penceremi (tk ile) kapattıktan sonra görünen zombi iplikleri ile ilgili sorunlar yaşıyordum. Popen'i yeniden tanımladığınız son snippet, sorunu çözdü. Python> 3.4 ile çalışan herkes için 'multiprocessing.forking' yerine' for multiprocessing.popen_spawn_win32 'forking' komutunu kullanmanız gerekir. –

+0

Unutmayın 'multiprocessing.freeze_support()' her zaman '__name__ == '__main __'' içindeki ilk satır olmalı ve bu satırın önünde başka bir kod yürütülmemelidir (örn. __name__ == '__main__ önce) ''). Bazı kodları yürüten bazı ithalatlar yaptım, sonuçta 'multiprocessing.freeze_support() 'herhangi bir etkiye sahip olmadı. – Guido

İlgili konular