Frage

Die Twisted-Dokumentation ließ mich glauben, dass es in Ordnung sei, Techniken wie zu kombinieren reactor.spawnProcess() Und threads.deferToThread() in der gleichen Anwendung, dass der Reaktor dies unter der Decke elegant bewältigen würde.Als ich es tatsächlich ausprobierte, stellte ich fest, dass meine Anwendung blockiert.Wenn Sie mehrere Threads alleine oder untergeordnete Prozesse alleine verwenden, ist alles in Ordnung.

Wenn ich in die Reaktorquelle schaue, stelle ich fest, dass SelectReactor.spawnProcess() Methode ruft einfach auf os.fork() ohne Rücksicht auf mehrere Threads, die möglicherweise ausgeführt werden.Dies erklärt die Deadlocks, denn beginnend mit dem Aufruf von os.fork() Sie haben zwei Prozesse mit mehreren gleichzeitig laufenden Threads, die wer weiß was mit denselben Dateideskriptoren tun.

Meine Frage an SO lautet: Was ist die beste Strategie zur Lösung dieses Problems?

Was ich im Sinn habe, ist eine Unterklasse SelectReactor, sodass es ein Singleton ist und aufruft os.fork() nur einmal, sofort bei der Instanziierung.Der untergeordnete Prozess wird im Hintergrund ausgeführt und fungiert als Server für den übergeordneten Prozess (unter Verwendung der Objektserialisierung über Pipes zur Hin- und Herkommunikation).Das übergeordnete Element führt die Anwendung weiterhin aus und kann nach Bedarf Threads verwenden.Anrufe an spawnProcess() im übergeordneten Prozess wird an den untergeordneten Prozess delegiert, der garantiert nur einen Thread ausführt und daher aufrufen kann os.fork() sicher.

Hat das schon mal jemand gemacht?Gibt es einen schnelleren Weg?

War es hilfreich?

Lösung 3

Als ich nach einiger Zeit auf dieses Problem zurückkam, stellte ich Folgendes fest:

reactor.callFromThread(reactor.spawnProcess, *spawnargs)

an Stelle von:

reactor.spawnProcess(*spawnargs)

dann verschwindet das Problem in meinem kleinen Testfall.In der Twisted-Dokumentation „Using Processes“ gibt es eine Bemerkung, die mich dazu veranlasst hat, Folgendes zu versuchen:„Der meiste Code in Twisted ist nicht threadsicher.Beispielsweise ist das Schreiben von Daten aus einem Protokoll in einen Transport nicht threadsicher.

Ich vermute, dass die anderen Personen, von denen Jean-Paul dieses Problem hatte, einen ähnlichen Fehler machen.Die Anwendung ist dafür verantwortlich, sicherzustellen, dass Reaktor- und andere API-Aufrufe im richtigen Thread erfolgen.Und anscheinend ist der „richtige Thread“, mit sehr wenigen Ausnahmen, fast immer der Hauptreaktor-Thread.

Andere Tipps

Was ist die beste Strategie zur Lösung dieses Problems?

Reichen Sie ein Ticket ein (vielleicht danach Registrieren) beschreibt das Problem, vorzugsweise mit einem reproduzierbaren Testfall (für maximale Genauigkeit).Dann kann es zu Diskussionen darüber kommen, wie (oder wie unterschiedliche Plattformen unterschiedliche Lösungen erfordern) der beste Weg (oder Wege) zur Umsetzung sein könnte.

Die Idee, sofort einen untergeordneten Prozess zu erstellen, um bei der weiteren Erstellung untergeordneter Prozesse zu helfen, wurde bereits früher geäußert, um das Leistungsproblem beim Ernten untergeordneter Prozesse zu lösen.Wenn dieser Ansatz nun zwei Probleme löst, sieht er etwas attraktiver aus.Eine mögliche Schwierigkeit bei diesem Ansatz besteht darin spawnProcess Gibt synchron ein Objekt zurück, das die PID des Kindes bereitstellt und das Senden von Signalen an dieses Objekt ermöglicht.Dies ist etwas aufwändiger zu implementieren, wenn ein Zwischenprozess im Weg steht, da die PID zuvor an den Hauptprozess zurückkommuniziert werden muss spawnProcess kehrt zurück.Eine ähnliche Herausforderung wird die Unterstützung des sein childFDs Argument, da es nicht mehr möglich sein wird, die Dateideskriptoren im untergeordneten Prozess lediglich zu erben.

Eine alternative Lösung (die möglicherweise etwas hackiger ist, aber möglicherweise auch weniger Herausforderungen bei der Implementierung mit sich bringt) könnte darin bestehen, anzurufen sys.setcheckinterval mit einer sehr großen Nummer vor dem Anruf os.fork, und stellen Sie dann das ursprüngliche Prüfintervall nur im übergeordneten Prozess wieder her.Dies sollte ausreichen, um einen Thread-Wechsel im Prozess bis zum zu vermeiden os.execvpe findet statt und zerstört alle zusätzlichen Threads.Dies ist nicht ganz richtig, da dadurch bestimmte Ressourcen (z. B. Mutexe und Bedingungen) in einem schlechten Zustand bleiben, Sie diese jedoch mit verwenden deferToThread ist nicht sehr häufig, also hat das vielleicht keinen Einfluss auf Ihren Fall.

Der Rat, den Jean-Paul in seiner Antwort gibt, ist gut, aber dieser sollen funktioniert (und tut es in den meisten Fällen).

Erstens verwendet Twisted Threads auch für die Auflösung von Hostnamen, und ich habe in Twisted-Prozessen definitiv Unterprozesse verwendet, die auch Clientverbindungen herstellen.Das kann also in der Praxis funktionieren.

Zweite, fork() erstellt nicht mehrere Threads im untergeordneten Prozess. Gemäß der Standardbeschreibung fork(),

Ein Prozess soll mit einem einzelnen Thread erstellt werden.Wenn ein Multithread-Prozess fork() aufruft, muss der neue Prozess eine Replik des aufrufenden Threads enthalten ...

Das heißt aber nicht, dass es welche gibt NEIN Mögliche Multithreading-Probleme mit spawnProcess;Der Standard sagt auch:

...Um Fehler zu vermeiden, darf der untergeordnete Prozess nur Async-Signal-sichere Operationen ausführen, bis eine der Exec-Funktionen aufgerufen wird ...

und ich glaube nicht, dass es irgendetwas gibt, das sicherstellt, dass nur asynchrone signalsichere Vorgänge verwendet werden.

Bitte beschreiben Sie Ihr genaues Problem genauer, da es sich nicht um einen Unterprozess handelt, bei dem Threads geklont werden.

fork() unter Linux lässt den untergeordneten Prozess definitiv nur mit einem Thread zurück.

Ich gehe davon aus, dass Sie wissen, dass bei der Verwendung von Threads in Twisted die EINZIGE Twisted-API, die Threads aufrufen dürfen, callFromThread ist?Alle anderen Twisted-APIs dürfen nur vom Haupt-Reaktor-Thread aufgerufen werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top