2017-06-22 5 views
2

Das Endziel besteht darin, eine Methode im Hintergrund auszuführen, aber nicht parallel: Wenn mehrere Objekte diese Methode aufrufen, sollte jeder warten, bis er an der Reihe ist. Um das Ausführen im Hintergrund zu erreichen, muss ich die Methode in einem Unterprozess (kein Thread) ausführen, und ich muss es mit spawn (nicht fork) starten. Um parallele Ausführungen zu verhindern, besteht die offensichtliche Lösung darin, eine globale Sperre zwischen den Prozessen zu verwenden.
Wenn Prozesse gegabelt werden, was bei Unix der Standard ist, ist es einfach zu erreichen, wie in den beiden folgenden Codes hervorgehoben.
Wir können es als Klassenvariable teilen:Python: Freigeben einer Sperre zwischen erzeugten Prozessen

import multiprocessing as mp 
from time import sleep 

class OneAtATime: 

    l = mp.Lock() 

    def f(self): 
     with self.l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    a = OneAtATime() 
    b = OneAtATime() 
    p1 = mp.Process(target = a.f) 
    p2 = mp.Process(target = b.f) 
    p1.start() 
    p2.start() 

Oder wir können es an die Methode übergeben:

import multiprocessing as mp 
from time import sleep 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = mp.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 

Beide Codes das entsprechende Verhalten von Druck „Hallo“ auf eine Sekunde haben von Intervall. Wenn Sie jedoch die start method auf "spawn" ändern, werden sie beschädigt.
Der erste (1) druckt beide "hallo" s gleichzeitig. Dies liegt daran, the internal state of a class is not pickled, so dass sie nicht die gleiche Sperre haben.
Die zweite (2) schlägt mit FileNotFoundError zur Laufzeit fehl. Ich denke, es hat damit zu tun, dass Schlösser nicht gebeizt werden können: siehe Python sharing a lock between processes.
In dieser Antwort werden zwei Fixes vorgeschlagen (Randnotiz: Ich kann keinen Pool verwenden, weil ich willkürlich eine beliebige Anzahl von Prozessen erstellen möchte).

import multiprocessing as mp 
from time import sleep 

if __name__ == "__main__": 
    mp.set_start_method('spawn') 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = m.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 

Dies schlägt mit Attribute und FileNotFoundError (3):
Ich habe keine Möglichkeit zur Anpassung der zweite Lösung, aber ich versuchte, zu implementieren, um den ersten gefunden. Tatsächlich schlägt es auch fehl (BrokenPipe), wenn die Fork-Methode verwendet wird (4).
Was ist die richtige Art, eine Sperre zwischen erzeugten Prozessen zu teilen?
Eine kurze Erklärung der vier Fehler, die ich nummeriert habe, wäre auch nett. Ich führe Python 3.6 unter Archlinux.

+0

Es klingt, als ob Sie die Dokumentation ziemlich sorgfältig gelesen haben. Haben Sie sich die Lösungen in [17.2.1.5] (https://docs.python.org/3.6/library/multiprocessing.html#sharing-state-between-processes) angesehen? Sie sollten in der Lage sein, das Schloss in den gemeinsamen Speicher oder einen Manager zu stecken, richtig? –

+0

Ich habe versucht, es ist in der dritten Code-Snippet. Es hat nicht funktioniert, aber es gibt wahrscheinlich viele Möglichkeiten, dies zu tun, einschließlich eines, das den Trick macht. – Zil0

+0

Entschuldigung, ich hätte den Code vor dem Kommentieren weiter studieren sollen. :-) –

Antwort

1

Der letzte Codeausschnitt funktioniert, vorausgesetzt das Skript wird nicht vorzeitig beendet. Fügeverfahren ist genug:

import multiprocessing as mp 
from time import sleep 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

if __name__ == "__main__": 
    mp.set_start_method('spawn') 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = m.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 
    p1.join() 
    p2.join() 

Mehr Informationen zu dem Fehler, der es verursacht wurde hier https://stackoverflow.com/a/25456494/8194503.

+0

Nun, da du das erwähnt hast, hätte ich sofort daran denken sollen. Gute Forschung, sowohl in der Frage als auch in der Antwort. –

1

Herzlichen Glückwunsch, Sie haben sich 90% des Weg dorthin. Der letzte Schritt ist eigentlich nicht sehr schwer.

Ja, Ihr endgültiger Codeblock schlägt mit einem AttributeError fehl, aber was genau ist der Fehler? Msgstr "Kann Attribut 'OneAtATime' nicht erhalten". Dies ist sehr ähnlich zu einem Problem, das Sie bereits kennen gelernt haben - es ist nicht die Klasse OneAtATime beizen.

Ich habe folgende Änderung und es hat funktioniert, wie Sie möchten:

file ooat.py:

from time import sleep 

class OneAtATime: 
    def f(self, l): 
     with l: 
      sleep(1) 
     print("Hello") 

interactive shell:

import multiprocessing as mp 
from oaat import OneAtATime 
if __name__ == "__main__": 
    mp.set_start_method('spawn') 
    a = OneAtATime() 
    b = OneAtATime() 
    m = mp.Manager() 
    l = m.Lock() 
    p1 = mp.Process(target = a.f, args = (l,)) 
    p2 = mp.Process(target = b.f, args = (l,)) 
    p1.start() 
    p2.start() 

Man merkt, kann ich alles tun, nicht wirklich - aufgeteilt nur Ihren Code in zwei separate Dateien. Probieren Sie es aus, Sie werden sehen, es funktioniert gut. (Zumindest hat es für mich mit python 3.5 auf ubuntu funktioniert.)

+0

Ich habe diese Antwort vorübergehend gelöscht, als ich gesehen habe, dass ich vergessen habe, sie mit der Spawn-Methode zu testen. Dann, als ich getestet habe, hieß es "OneAtATime nicht definiert", aber nur, weil ich die Zeile "from ooat import OneAtATime" vergessen habe. So, jetzt habe ich anscheinend Gabel und Spawn getestet, und es funktioniert gut. –

+0

Antwort ist in Ihrem Beitrag, aber nicht in Ihrem Post. Es ist "interaktive Shell". Der Fehler, den ich bekam, war: AttributError: 'ForkAwareLocal' Objekt hat kein Attribut 'Verbindung'. Dort hörte ich auf zu versuchen und schrieb die Frage, aber ich war ein Googling weg von der tatsächlichen Lösung: https://Stackoverflow.com/a/25456494/8194503. Es funktioniert perfekt in der gleichen Datei für mich, nicht sicher, was Ihr Fehler war. Ich werde meine Frage mit dem Fix bearbeiten. Ich bin mir auch nicht sicher, was die Etikette ist, ob ich deine Antwort akzeptiere, ich nehme an, sie akzeptiert sie trotzdem. Vielen Dank für Ihre Zeit ! – Zil0

+1

Wenn du dein Problem gelöst hast und ich es nicht getan habe, solltest du deine Antwort tatsächlich posten, warten, wann auch immer es dir sagt, und deine eigene Antwort akzeptieren. Das ist völlig in Ordnung, und im Laufe der Zeit werden Sie Ihre Antwort bewerten. –

Verwandte Themen