Ich versuche ein Programm zu erstellen, das einen Prozesspool von, sagen wir, 5 Prozessen startet, einige Operationen ausführt und dann beendet, aber die 5 Prozesse offen lässt. Später kann der Benutzer das Programm erneut ausführen, und anstatt es neue Prozesse zu starten, verwendet es die vorhandenen 5. Im Grunde ist es ein Producer-Consumer-Modell, wo:
Ich verwende das eingebaute multiprocessing
-Modul, derzeit in Python 2.6.4, aber mit der Absicht, irgendwann zu 3.1.1 zu wechseln.
Hier ist ein grundlegendes Anwendungsszenario:
program.py operation
- ein Produzent, fünf Verbraucher laufen. program.py operation
- ein Produzent, fünf Verbraucher laufen. program.py operation
- zwei Produzenten, fünf Verbraucher laufen. program.py stop
und schließt ab - es laufen keine Prozesse. program.py start
und schließt ab - fünf laufende Kunden. program.py operation
- ein Produkt, fünf Verbraucher laufen. program.py stop
und schließt ab - es laufen keine Prozesse. Das Problem, das ich habe, ist, dass ich nicht weiß, wo ich anfangen soll:
Sobald ich das kann, weiß ich, wie man die Prozesse verwaltet. Es muss einen zuverlässigen Weg geben, um existierende Prozesse zu erkennen, da ich gesehen habe, dass Firefox dies tut, um die Ausführung mehrerer Firefox-Instanzen zu verhindern, aber ich habe keine Ahnung, wie man das in Python macht.
Es gibt eine Reihe gängiger Methoden für die Ausführung von Punkt 1 (Erkennung laufender Prozesse). Um sie jedoch zu verwenden, müssen Sie zunächst Ihre Vorstellung davon, wie diese Hintergrundprozesse beim ersten Aufruf des Programms gestartet werden, geringfügig anpassen .
Denken Sie daran, dass das erste Programm nicht die fünf Prozesse startet und dann beendet, sondern dass es erkennt, dass es die erste Instanz ist, die gestartet wird und nicht . Es kann eine Dateisperre (eine der üblichen Vorgehensweisen zum Verhindern, dass eine Anwendung mehrfach vorkommt) oder nur zu einem Socket (ein anderer allgemeiner Ansatz) erstellt werden. Beide Ansätze lösen in einer zweiten Instanz eine Ausnahme aus, die dann weiß, dass sie nicht die erste ist und ihre Aufmerksamkeit wieder auf die erste Instanz lenken kann.
Wenn Sie multiprocessing
verwenden, sollten Sie einfach den Manager
Das erste Programm startet die Prozesse, erstellt Warteschlangen, Proxies oder was auch immer. Es erstellt einen Manager, um den Zugriff auf sie zu ermöglichen, möglicherweise Fernzugriff .
Nachfolgende Aufrufe versuchen zuerst, den Server / Manager auf dem vordefinierten Socket zu kontaktieren (oder andere Techniken zu verwenden, um den Socket zu ermitteln, auf dem er sich befindet). Anstatt einen server_forever()
-Aufruf auszuführen, verwenden sie connect()
und kommunizieren mit den üblichen multiprocessing
-Mechanismen.
Sehen Sie sich diese verschiedenen Service Discovery-Mechanismen an: Ссылка
Die Grundidee ist, dass die Verbraucher beim Start jeweils einen Service registrieren. Der Hersteller würde beim Start den Erkennungsprozess durchlaufen. Wenn es die Verbraucher findet, bindet es sich an sie. Wenn es sie nicht findet, startet es neue Verbraucher. In den meisten dieser Systeme können Dienste normalerweise auch Eigenschaften veröffentlichen, so dass sich jeder Benutzer eindeutig identifizieren und dem entdeckenden Hersteller andere Informationen geben kann.
Bonjour / zeroconf wird ziemlich plattformübergreifend unterstützt. Sie können Safari sogar so konfigurieren, dass Ihnen die Zeroconf-Dienste in Ihrem lokalen Netzwerk angezeigt werden. Sie können damit die Dienstanzeige für die Verbraucher debuggen. Ein Seitenvorteil dieser Art von Ansatz ist, dass Sie die Produzenten leicht auf verschiedenen Maschinen als die Verbraucher laufen lassen können.
Sie benötigen ein Client-Server-Modell auf einem lokalen System. Sie können dies über TCP / IP-Sockets tun, um zwischen Ihren Clients und Servern zu kommunizieren, aber es ist schneller, lokale Named Pipes zu verwenden, wenn Sie nicht über ein Netzwerk kommunizieren müssen.
Die grundlegenden Voraussetzungen für Sie, wenn ich richtig verstanden habe, sind diese:
1. Ein Produzent sollte in der Lage sein, Verbraucher zu spawnen, wenn es noch keine gibt.
2. Ein Hersteller sollte in der Lage sein, mit den Verbrauchern zu kommunizieren.
3. Ein Produzent sollte in der Lage sein, bereits bestehende Konsumenten zu finden und mit ihnen zu kommunizieren.
4. Selbst wenn ein Produzent fertig ist, sollten die Konsumenten weiterlaufen.
5. Mehr als ein Hersteller sollte mit den Verbrauchern kommunizieren können.
Lasst uns jeden einzeln angehen:
(1) ist ein einfaches Prozesserzeugungsproblem, mit dem Unterschied, dass Consumer (Child) -Prozesse weiter ausgeführt werden sollten, selbst wenn der Producer (Eltern) beendet wird. Siehe (4) unten.
(2) Ein Produzent kann über Named Pipes mit den Kunden kommunizieren. Siehe os.mkfifo () und unix man-Seite von mkfifo () erstellen Named Pipes.
(3) Sie müssen Named Pipes aus den Consumer-Prozessen in einem bekannten Pfad erstellen, wenn sie gestartet werden. Der Hersteller kann herausfinden, ob irgendwelche Verbraucher auf der Suche nach diesem bekannten Rohr am selben Ort sind. Wenn die Pfeife (n) nicht existieren, laufen keine Verbraucher und die Produzenten können diese spawnen.
(4) Dazu müssen Sie os.setuid () verwenden und den Consumer erstellen Prozesse verhalten sich wie ein Daemon. Siehe unix man-Seite von setsid ().
(5) Dieser ist knifflig. Mehrere Hersteller können mit der gleichen Named Pipe mit den Konsumenten kommunizieren, aber Sie können nicht mehr als "PIPE_BUF" übertragen Datenmenge vom Hersteller zum Verbraucher, wenn Sie zuverlässig identifizieren möchten, welcher Hersteller die Daten gesendet hat, oder wenn Sie eine Art Verschachtelung von Daten verschiedener Hersteller verhindern möchten.
Ein besserer Weg (5) besteht darin, dass die Benutzer bei der Ausführung eine "control" genannte Pipe (/tmp/control.3456, 3456, die die Verbraucher-PID ist) öffnen. Hersteller haben zuerst einen Kommunikationskanal eingerichtet, indem sie die Pipe "control" benutzten. Wenn ein Hersteller eine Verbindung herstellt, sendet er seine PID "1234" an den Verbraucher über die "control" -Pipe, die dem Verbraucher mitteilt, dass er eine Named Pipe für den Datenaustausch mit dem Hersteller erstellen soll, etwa "/tmp/data.1234". Dann schließt der Produzent die "Kontroll" -Röhre und öffnet "/tmp/data.1234", um mit dem Verbraucher zu kommunizieren. Jeder Verbraucher kann seine eigenen "Kontroll" -Rohre haben (die Verbraucher-PIDs verwenden, um zwischen den Pipes verschiedener Verbraucher zu unterscheiden), und jeder Erzeuger erhält seine eigene "Daten" -Röhre. Wenn ein Producer beendet , sollte es seine Datenleitung bereinigen oder dem Verbraucher mitteilen, dies zu tun. Wenn der Verbraucher fertig ist, sollte er seine Steuerleitungen reinigen.
Eine Schwierigkeit besteht darin, zu verhindern, dass sich mehrere Hersteller gleichzeitig mit den Steuerrohren eines einzelnen Verbrauchers verbinden. Die "Kontroll" -Röhre ist hier eine geteilte Ressource und Sie müssen zwischen verschiedenen Herstellern synchronisieren, um darauf zuzugreifen. Verwenden Sie dafür Semaphoren oder Dateisperrung . Sehen Sie hierzu das posix_ipc Python-Modul.
Hinweis: Ich habe die meisten der oben genannten allgemeinen UNIX-Semantiken beschrieben, aber alles, was Sie wirklich brauchen, ist die Fähigkeit, Daemon-Prozesse zu erstellen, die Möglichkeit, "benannte" Pipes / Queues / was auch immer zu erstellen ein nicht verwandter Prozess und die Fähigkeit, zwischen nicht verwandten Prozessen zu synchronisieren. Sie können jedes Python-Modul verwenden, das eine solche Semantik bietet.
Tags und Links python multiprocessing