Sie haben bereits eine ganze Reihe von Antworten erhalten, von "gefälschten Threads" bis hin zu externen Frameworks, aber ich habe niemanden erwähnt Queue.Queue
- die "geheime Sauce" des CPython-Threadings.
Zum Erweitern: Solange Sie keine reine Python-CPU-lastige Verarbeitung überlappen müssen (in diesem Fall benötigen Sie dies multiprocessing
- aber es kommt auch mit einer eigenen Queue
Implementierung, sodass Sie mit einigen erforderlichen Vorsichtsmaßnahmen den allgemeinen Rat I anwenden können Ich gebe ;-), Pythons eingebautes threading
wird es tun ... aber es wird es viel besser machen, wenn Sie es mit Bedacht verwenden , z. B. wie folgt.
Shared Memory "vergessen", angeblich das Hauptvorteil von Threading und Multiprocessing - es funktioniert nicht gut, es skaliert nicht gut, hat es nie, wird es nie. Verwenden Sie den gemeinsam genutzten Speicher nur für Datenstrukturen, die einmal eingerichtet wurden, bevor Sie Sub-Threads erzeugen, und die danach nie mehr geändert wurden. Machen Sie für alles andere einen einzelnen Thread für diese Ressource verantwortlich und kommunizieren Sie mit diesem Thread über Queue
.
Weisen Sie jeder Ressource, die Sie normalerweise durch Sperren schützen möchten, einen speziellen Thread zu: eine veränderbare Datenstruktur oder eine zusammenhängende Gruppe davon, eine Verbindung zu einem externen Prozess (einer Datenbank, einem XMLRPC-Server usw.), einer externen Datei usw. Richten Sie einen kleinen Thread-Pool für allgemeine Aufgaben ein, für die keine dedizierte Ressource dieser Art vorhanden ist oder benötigt wird. Erstellen Sie keine Threads nach Bedarf, da Sie sonst durch den Overhead beim Wechseln der Threads überfordert werden.
Die Kommunikation zwischen zwei Threads erfolgt immer über Queue.Queue
- eine Form der Nachrichtenübermittlung, die einzige vernünftige Grundlage für die Mehrfachverarbeitung (neben dem vielversprechenden Transaktionsspeicher, für den ich jedoch keine produktionswürdigen Implementierungen außer In Haskell kenne).
Jeder dedizierte Thread, der eine einzelne Ressource (oder einen kleinen zusammenhängenden Satz von Ressourcen) verwaltet, wartet auf Anforderungen in einer bestimmten Queue.Queue-Instanz. Threads in einem Pool warten auf eine einzelne gemeinsam genutzte Queue.Queue (Queue ist solide threadsicher und wird Sie dabei nicht scheitern lassen).
Threads, die nur eine Anforderung in einer Warteschlange (gemeinsam genutzt oder dediziert) in die Warteschlange stellen müssen, tun dies, ohne auf Ergebnisse zu warten, und fahren fort. Threads, die möglicherweise ein Ergebnis oder eine Bestätigung für eine Anforderungswarteschlange benötigen, erhalten ein Paar (Anforderung, Empfangswarteschlange) mit einer Instanz von Queue.Queue, die sie gerade erstellt haben, und schließlich, wenn die Antwort oder Bestätigung für das Fortfahren unabdingbar ist, erhalten sie (Warten) ) aus ihrer Empfangswarteschlange. Stellen Sie sicher, dass Sie bereit sind, sowohl Fehlerantworten als auch echte Antworten oder Bestätigungen zu erhalten (Twisted's deferred
sind hervorragend darin, diese Art von strukturierter Antwort zu organisieren, übrigens!).
Sie können Queue auch verwenden, um Instanzen von Ressourcen zu "parken", die von einem beliebigen Thread verwendet werden können, jedoch niemals von mehreren Threads gleichzeitig gemeinsam genutzt werden (DB-Verbindungen mit einigen DBAPI-Komponenten, Cursor mit anderen usw.). Auf diese Weise können Sie sich entspannen Die Anforderung eines dedizierten Threads zugunsten von mehr Pooling (ein Pool-Thread, der aus der gemeinsam genutzten Warteschlange eine Anforderung erhält, die eine in der Warteschlange befindliche Ressource benötigt, erhält diese Ressource aus der entsprechenden Warteschlange und wartet bei Bedarf usw. usw.).
Twisted ist eigentlich eine gute Möglichkeit, dieses Menuett (oder den Square Dance) zu organisieren, nicht nur dank verzögerter, sondern auch aufgrund seiner soliden, soliden, hoch skalierbaren Basisarchitektur: Sie können Dinge so arrangieren, dass Threads oder Unterprozesse nur dann verwendet werden, wenn wirklich gerechtfertigt, während die meisten Dinge, die normalerweise als threadwürdig angesehen werden, in einem einzigen ereignisgesteuerten Thread ausgeführt werden.
Aber mir ist klar, dass Twisted nicht jedermanns Sache ist - der Ansatz "Ressourcen widmen oder bündeln, Warteschlange im Wazoo verwenden, niemals etwas tun, das eine Sperre benötigt, oder, wie Guido verbietet, ein noch weiter fortgeschrittenes Synchronisationsverfahren wie Semaphor oder Bedingung" kann Wird auch dann verwendet, wenn Sie sich nicht mit asynchronen ereignisgesteuerten Methoden auseinandersetzen können, und bietet dennoch mehr Zuverlässigkeit und Leistung als jeder andere weit verbreitete Threading-Ansatz, auf den ich jemals gestoßen bin.