1

Ich arbeite mit Python (2.7) und pymongo (3.3), und ich muss einen Kindprozess spawnen, um einen Job asynchron auszuführen. Leider ist Pymongo nicht Forksafe wie beschrieben here (und ich muss mit der Db interagieren, bevor ich den Child-Prozess erzeugen).Spawn einen Prozess in Python ohne Forking

lief ich ein Experiment mit subprocess.Popen (mit shell Set True und dann False) und multiprocessing.Process. So weit ich sagen kann, geben beide den Parent-Prozess aus, um den Child-Prozess zu erstellen, aber nur multiprocessing.Process bewirkt, dass Pymongo seine Warnung ausgibt, dass es einen gegabelten Prozess erkannt hat.

Ich frage mich, was die pythonische Art, dies zu tun ist. Es scheint, dass vielleicht os.system wird es für mich tun, aber subprocess ist als ein beabsichtigter Ersatz für os.system beschrieben, so frage ich mich, ob ich etwas vermisse.

Antwort

4

Ich denke, Sie missverstehen; Da die Dokumentation von PyMongo Sie warnt, dass ein einzelner MongoClient nicht forksicher ist, interpretieren Sie das so, dass PyMongo verbietet, dass Ihr gesamtes Programm jemals Subprozesse erstellt.

Jeder einzelne MongoClient ist nicht forksicher, dh Sie müssen ihn nicht vor dem Verzweigen erstellen und nach dem Verzweigen dasselbe MongoClient-Objekt verwenden. Die Verwendung von PyMongo in Ihrem Programm insgesamt oder die Verwendung eines MongoClients vor einer Verzweigung und einer anderen danach sind sicher.

Deshalb ist subprocess.Popen ok: Sie fork, dann exec (um Ihr Programm durch ein anderes im untergeordneten Prozess zu ersetzen), und deshalb können Sie möglicherweise nicht den gleichen MongoClient im Kind später verwenden.

Gabel (mit dem Multiprocessing-Modul laicht Prozesse Auf Unix-Systemen):

die PyMongo FAQ zu zitieren. Vorsicht ist geboten, wenn Instanzen von MongoClient mit fork() verwendet werden. Insbesondere dürfen Instanzen von MongoClient nicht von einem übergeordneten Prozess in einen untergeordneten Prozess kopiert werden. Stattdessen müssen der Elternprozess und die einzelnen untergeordneten Prozesse eigene Instanzen von MongoClient erstellen. Zum Beispiel:

# Each process creates its own instance of MongoClient. 
def func(): 
    db = pymongo.MongoClient().mydb 
    # Do something with db. 

proc = multiprocessing.Process(target=func) 
proc.start() 

dies nie tun:

client = pymongo.MongoClient() 

# Each child process attempts to copy a global MongoClient 
# created in the parent process. Never do this. 
def func(): 
    db = client.mydb 
    # Do something with db. 

proc = multiprocessing.Process(target=func) 
proc.start() 

Instanzen MongoClient aus dem übergeordneten Prozess mit hohen Wahrscheinlichkeit Deadlock im Kindprozess aufgrund inhärenten haben kopiert Inkompatibilitäten zwischen fork(), Threads und Sperren. PyMongo wird versuchen, eine Warnung auszugeben, wenn die Gefahr besteht, dass dieser Deadlock auftritt.

+0

Aha macht Sinn. Ich war auch neugierig auf mögliche Nebenwirkungen von erben Dateideskriptoren (insbesondere Socket-Handles) im Kind-Prozess, aber ich denke, Popens close_fds Argument Adressen, die auch – nonagon

+0

PyMongo erstellt seine Sockets mit FD_CLOEXEC, so dass diese Deskriptoren geschlossen sind, ob Sie close_fds übergeben oder nicht. –

0

Es gibt absolut keine Möglichkeit, einen neuen Prozess zu erstellen, ohne Fork in der * NIX-Umgebung zu verwenden. Tatsächlich sind alle Prozesse, die innerhalb des Systems ablaufen, eine Abzweigung des Init-Prozesses. Von dem, was ich verstehe, ist es nicht sicher, den sicheren pymongo Handgriff zwischen Gabeln zu benutzen. Sie können entweder versuchen, Threads anstelle von Prozessen zu erzeugen, oder Pymongo von den Kindgabeln aus öffnen.

2

Wenn Sie zu Python 3.4 oder höher wechseln können, können Sie vor der Verwendung von pymongo Ihre multiprocessing start method auf setzen. Das verzweigt einen Gabelserver-Prozess sofort, und alle zukünftige Verwendung von multiprocessing Gabeln, die Server, nicht Ihren Hauptprozess verzweigen. Sobald der Fork-Server eingerichtet ist, kann Ihr Hauptprozess pymongo verwenden, der Fork-Server wird ihn nicht verwendet haben, so dass es keine Probleme beim Forken gibt.

Leider wurden Startmethoden nur in 3.4 hinzugefügt, also ist es keine Option für 2.7, aber wenn jemand anderes dieses Problem hat, könnte es für sie nützlich sein.

+0

Ja das ist genau die Art von Ding, auf die ich gehofft hatte. Ich sehe jetzt, dass Pymongo nach fork/exec funktionieren wird, aber das Forken eines frischen, nicht verwandten Prozesses fühlt sich an wie eine sauberere Methode, Kindprozesse zu erzeugen (vielleicht werden meine Windows-Wurzeln angezeigt). Leider fühlt sich Python 2-> 3 so an, als wäre es noch ein paar Jahre für unsere Codebase :( – nonagon

5

Nicht fork sicher bedeutet nicht, dass Sie fork nicht aufrufen können ... Es bedeutet nur, dass der untergeordnete Prozess keine geerbte PyMongo-Instanz verwenden soll. Wenn Sie subprocess.Popen verwenden, ruft das neu verzweigte Kind fast sofort exec auf, um durch eine Shellinstanz (Shell = True) oder die erforderliche ausführbare Datei (Shell = False) ersetzt zu werden. So ist es sicher aus der Sicht von PyMongo.

Umgekehrt, wenn Sie multiprocessing.Process aufrufen, ist das Kind tatsächlich eine Kopie des Elternteils und behält seine PyMongo-Instanzen. Daher ist es unsicher, PyMongo in diesem Kontext zu verwenden, und die Warnmeldung wurde korrekt ausgegeben.

Verwandte Themen