2017-03-11 3 views
0

Wenn ich eine multiprocessing.Queue oder eine multiprocessing.Manager (oder eines der anderen Synchronisationsprimitiven) teilen muss, gibt es einen Unterschied, indem Sie sie auf der globalen (Modul) definieren Ebene, im Gegensatz zu ihnen als Argument für die Funktion in einem anderen Prozess ausgeführt werden?Gemeinsame Nutzung von Synchronisationsobjekten über globalen Namespace vs als Funktionsargument

Zum Beispiel sind hier drei Möglichkeiten, ich kann eine Warteschlange vorstellt geteilt werden könnte:

# works fine on both Windows and Linux 
from multiprocessing import Process, Queue 

def f(q): 
    q.put([42, None, 'hello']) 

def main(): 
    q = Queue() 
    p = Process(target=f, args=(q,)) 
    p.start() 
    print(q.get()) # prints "[42, None, 'hello']" 
    p.join() 

if __name__ == '__main__': 
    main() 

gegen

# works fine on Linux, hangs on Windows 
from multiprocessing import Process, Queue 
q = Queue() 

def f(): 
    q.put([42, None, 'hello']) 

def main(): 
    p = Process(target=f) 
    p.start() 
    print(q.get()) # prints "[42, None, 'hello']" 
    p.join() 

if __name__ == '__main__': 
    main() 

gegen

# works fine on Linux, NameError on Windows 
from multiprocessing import Process, Queue 

def f(): 
    q.put([42, None, 'hello']) 

def main(): 
    p = Process(target=f) 
    p.start() 
    print(q.get()) # prints "[42, None, 'hello']" 
    p.join() 

if __name__ == '__main__': 
    q = Queue() 
    main() 

Welche den richtigen Ansatz ? Ich schätze aus meinen Experimenten, dass es nur der erste ist, aber ich wollte bestätigen, dass es offiziell der Fall ist (und nicht nur für Queue, sondern für Manager und andere ähnliche Objekte).

+0

Die zweite sollte globale q am Anfang von f() haben? Die erste ist meiner Meinung nach die beste, nur weil die Objekte den richtigen Umfang haben, aber es ist nur eine Frage des Stils * IF * sie sind wirklich Singletons, und niemand ändert jemals den Code. Ein weiterer Grund, sie als Argumente zu übergeben, ist der Nachweis, dass Sie sie haben, und daher die Funktion korrekt aufrufen, obwohl dies besser in C++ und anderen stark typisierten Sprachen funktioniert, in denen der Compiler Buchhaltungsfehler für Sie auffängt, wenn Sie guten Stil verwenden. –

+0

"global q" ist nicht erforderlich, wenn Sie ein globales Objekt nur über einen Methodenaufruf ändern, anstatt die Variable neu zuzuordnen, um auf ein neues Objekt zu verweisen. Wie gesagt, es scheint, dass 2 von 3 auf Windows nicht funktionieren, also denke ich, dass es keine Frage des Stils ist. Ich konnte einfach keine klare Erklärung in den Dokumenten finden, aber es scheint, dass das Überschreiten eines Parameters die einzige zuverlässige Technik ist. – max

+0

Dies kann helfen: http://StackOverflow.com/Questions/37244168/Multiprocessing-Queue-Get-Hangs –

Antwort

1

Wie in den programming guidelines erwähnt

Explizit Ressourcen Kind-Pass-Prozesse

Auf Unix die Gabel Startmethode verwendet wird, ein Kind-Prozess Verwendung einer gemeinsam genutzten Ressource in einem übergeordneten Prozess erstellt machen kann ein mit globale Ressource. Es ist jedoch besser, das Objekt als Argument an den Konstruktor für den untergeordneten Prozess zu übergeben.

Abgesehen davon, dass der Code (potenziell) kompatibel mit Windows und den anderen Startmethoden ist, stellt dies auch sicher, dass solange der Kindprozess noch aktiv ist, das Objekt nicht im übergeordneten Prozess gesammelt wird. Dies kann wichtig sein, wenn eine Ressource freigegeben wird, wenn das Objekt im übergeordneten Prozess als Garbage Collection erfasst wird.

Das Problem ist die Art und Weise der Spawn/Forkserver (Windows unterstützt nur Spawn) funktioniert unter der Haube. Anstatt den übergeordneten Prozess mit seinen Speicher- und Dateientschlüsslern zu klonen, erzeugt er einen neuen Prozess von Grund auf. Anschließend lädt es einen neuen Python-Interpreter, der die zu importierenden Module übergibt und startet. Dies bedeutet natürlich, dass Ihre globale Variable eine brandneue Warteschlange sein wird, anstatt die der Eltern.

Eine weitere Implikation ist, dass die Objekte, die Sie an den neuen Prozess übergeben möchten, picklebar sein müssen, da sie durch eine Pipe geleitet werden.

+0

Hmm Ich bin mir wirklich nicht sicher, was diese Richtlinien zu sagen versuchen. Kannst du einen Blick auf die Nachricht werfen, die ich [hier] gepostet habe (http://bugs.python.org/msg289505)? – max

+0

Sorry, ich habe die falsche Richtlinie zitiert. Mein Fehler. Ich habe die Antwort aktualisiert. Ich entschuldige mich nochmal. – noxdafox

0

zusammenfassend einfach die answer from Davin Potts:

Die einzige tragbare Lösung ist, indem sie als Argument Queue() und Manager().* Objekte zu teilen - nie als globale Variablen. Der Grund dafür ist, dass unter Windows alle globalen Variablen neu erstellt (anstatt kopiert) werden, indem man den Code von Anfang an buchstäblich ausführt (very little information wird tatsächlich vom übergeordneten Prozess in den untergeordneten Prozess kopiert); So würde ein brandneues Queue() erstellt werden und natürlich (ohne irgendeine unerwünschte und verwirrende Magie) kann es unmöglich mit dem Queue() im Elternprozess verbunden werden.

Mein Verständnis ist, dass es keinen Nachteil zu übergeben Queue(), etc. als Parameter gibt; Ich kann keinen Grund finden, warum jemand eine nicht-portable Lösung mit globalen Variablen verwenden möchte, aber natürlich kann ich mich irren.

Verwandte Themen