Frage

Welche Module werden zum Schreiben von Multi-Thread-Anwendungen in Python verwendet? Ich bin mir der grundlegenden Parallelitätsmechanismen der Sprache und auch der von der Sprache bereitgestellten und auch von Stackloser Python, aber was sind ihre jeweiligen Stärken und Schwächen?

War es hilfreich?

Lösung

In der Reihenfolge der zunehmenden Komplexität:

Verwenden Sie das Threading -Modul

Profis:

  • Es ist wirklich einfach, jede Funktion (in der Tat) in seinem eigenen Thread auszuführen.
  • Das Teilen von Daten ist, wenn nicht einfach (Schlösser sind nie einfach :), zumindest einfach.

Nachteile:

  • Wie erwähnt von Juergen Python -Threads können im Dolmetscher nicht gleichzeitig zugreifen (es gibt ein großes Schloss, das berüchtigte Global Interpreter Lock.) Was das in der Praxis bedeutet, dass Threads für E/A -gebundene Aufgaben (Vernetzung, Schreiben auf die Festplatte usw.) nützlich sind, aber überhaupt nicht nützlich für die gleichzeitige Berechnung.

Verwenden Sie das Multiprozessierung Modul

Im einfachen Anwendungsfall sieht dies genau wie die Verwendung aus threading Außer jeder Aufgabe wird in ihrem eigenen Prozess nicht in ihrem eigenen Thread ausgeführt. (Fast buchstäblich: Wenn Sie nehmen Elis Beispiel, und ersetzen threading mit multiprocessing, Thread, mit Process, und Queue (das Modul) mit multiprocessing.Queue, es sollte gut laufen.)

Profis:

  • Tatsächliche Parallelität für alle Aufgaben (keine globale Interpreter -Sperre).
  • Skalen zu mehreren Prozessoren können sogar auf mehrere skalieren Maschinen.

Nachteile:

  • Prozesse sind langsamer als Fäden.
  • Die Datenaustausch zwischen den Prozessen ist schwieriger als bei Threads.
  • Gedächtnis wird nicht implizit geteilt. Sie müssen es entweder explizit teilen oder Sie müssen Variablen überlegen und sie hin und her senden. Das ist sicherer, aber schwieriger. (Wenn es zunehmend wichtig ist, scheinen die Python -Entwickler Menschen in diese Richtung zu drängen.)

Verwenden Sie ein Ereignismodell, wie z. Verdrehte

Profis:

  • Sie erhalten eine extrem gute Kontrolle über Priorität, was wann ausgeführt wird.

Nachteile:

  • Selbst mit einer guten Bibliothek ist die asynchrone Programmierung normalerweise schwieriger als mit Thread -Programmen, sowohl im Hinblick auf das Verständnis, was passieren soll, als auch im Hinblick auf das Debuggen, was tatsächlich passiert.

Im alle Ich gehe davon aus, dass Sie bereits viele der Probleme verstehen, die mit Multitasking verbunden sind, insbesondere das schwierige Problem, wie Daten zwischen Aufgaben austausch werden können. Wenn Sie aus irgendeinem Grund nicht wissen, wann und wie Sie Schlösser und Bedingungen verwenden, müssen Sie damit beginnen. Der Multitasking -Code ist voller Feinheiten und Gotchas, und es ist wirklich am besten, vor Beginn ein gutes Verständnis für Konzepte zu haben.

Andere Tipps

Sie haben bereits eine Reihe von Antworten erhalten, von "gefälschten Threads" bis zu externen Frameworks, aber ich habe gesehen, dass niemand erwähnt wurde Queue.Queue - Die "geheime Sauce" des CPython-Threading.

Um zu erweitern: solange Sie sich nicht überlappen müssen, dass die CPU-strenge Verarbeitung reine Python-CPU-Laste (in diesem Fall benötigen Sie multiprocessing - aber es kommt mit sich selbst Queue Auch die Implementierung, sodass Sie mit einigen benötigten Vorsichtsmaßnahmen den allgemeinen Rat anwenden können, den ich gebe ;-), Pythons integriertes Integrieren threading wird tun ... aber es wird es viel besser machen, wenn Sie es verwenden mit Bedacht, zB wie folgt.

"Vergessen" Shared Memory, angeblich das Haupt -Plus von Threading vs Multiprocessing - es funktioniert nicht gut, es skaliert nicht gut, hat nie, wird es nie tun. Verwenden Sie den gemeinsamen Speicher nur für Datenstrukturen, die einmal eingerichtet sind Vor Sie laichen Sub-Threads und haben sich danach nie geändert-für alles andere machen Sie eine Single Thread verantwortlich für diese Ressource und kommunizieren Sie mit diesem Thread über Queue.

Widmen Sie einen spezialisierten Thread für jede Ressource, die Sie normalerweise durch Schlösser schützen möchten: eine veränderliche Datenstruktur oder eine zusammenhängende Gruppe davon, eine Verbindung zu einem externen Prozess (ein DB, ein XMLRPC -Server usw.), eine externe Datei usw. usw. Holen Sie sich einen kleinen Thread -Pool für Allzweckaufgaben, die keine eigene Ressource dieser Art haben oder benötigen - nicht Spawn-Fäden wie und bei Bedarf, oder der Überkopf des Fadenschwesters wird Sie überwältigen.

Die Kommunikation zwischen zwei Threads erfolgt immer über Queue.Queue -Eine Form des Nachrichtenübergangs, die einzige vernünftige Grundlage für die Multiprozessierung (neben dem transaktionalen Memory, das vielversprechend ist, für die ich jedoch keine produktionswürdigen Implementierungen kenne, außer in Haskell).

Jeder dedizierte Thread, der eine einzelne Ressource (oder eine kleine Kohäsionsmenge von Ressourcen) verwaltet, hört auf Anfragen in einer bestimmten Warteschlange zu. Fäden in einem Pool warten in einer einzelnen gemeinsam genutzten Warteschlange. Gewohnheit scheitern Sie in diesem).

Themen, die nur eine Anfrage in einer Warteschlange stellen müssen (freigegeben oder dediziert), ohne auf die Ergebnisse zu warten, und weitermachen. Themen, die irgendwann ein Ergebnis oder eine Bestätigung für eine Anforderung an eine Warteschlange (Anfrage, Empfangsqueue) mit einer Instanz der Warteschlange benötigen. ) aus ihrem Empfang. Stellen Sie sicher, dass Sie bereit sind, Fehlerreaktionen sowie echte Antworten oder Bestätigungen zu erhalten (Twisted's deferredS eignen sich hervorragend darin, diese Art von strukturierter Reaktion zu organisieren, übrigens!).

Sie können auch Warteschlangen verwenden, um Ressourceninstanzen zu "Park", die von einem Thread verwendet werden können, aber niemals zwischen mehreren Threads gleichzeitig geteilt werden (DB -Verbindungen mit einigen DBAPI -Kompoenten, Cursors mit anderen) - dies ermöglicht Ihnen, sich zu entspannen Die Anforderung an das Dedized-Thread zugunsten von mehr Pooling (ein Pool-Thread, der von der gemeinsam genutzten Warteschlange erhält, die eine anhörbare Ressource benötigt, erhält diese Ressource von der AppPit-Warteschlange, bei Bedarf usw. Warten usw.).

Twisted ist eigentlich eine gute Möglichkeit, diesen Minuet (oder einen quadratischen Tanz so zu organisieren), nicht nur dank Aufschaltungen, sondern aufgrund seines soliden, soliden, hochskalierbaren Basisarchitektur: Sie können Dinge für die Verwendung von Threads oder Unterprozessen nur dann anordnen, wenn Wirklich garantiert, während die meisten Dinge in einem einzigen ereignisgesteuerten Thread normalerweise als Thread-würdig angesehen werden.

Mir ist jedoch festgestellt Sie werden immer noch verwendet, auch wenn Sie Ihren Kopf einfach nicht mit asynchronen ereignisgesteuerten Methoden umwickeln können und immer noch mehr Zuverlässigkeit und Leistung liefern als jeder andere weit verbreitete Threading-Ansatz, auf den ich jemals gestoßen bin.

Es hängt davon ab, was Sie tun möchten, aber ich bin teilweise darauf, nur das zu verwenden threading Modul in der Standardbibliothek, da dies wirklich einfach ist, jede Funktion zu übernehmen und sie einfach in einem separaten Thread auszuführen.

from threading import Thread

def f():
    ...

def g(arg1, arg2, arg3=None):
    ....

Thread(target=f).start()
Thread(target=g, args=[5, 6], kwargs={"arg3": 12}).start()

Usw. Ich habe oft einen Produzenten-/Verbraucher -Setup mit einer synchronisierten Warteschlange, die von der bereitgestellt wird Queue Modul

from Queue import Queue
from threading import Thread

q = Queue()
def consumer():
    while True:
        print sum(q.get())

def producer(data_source):
    for line in data_source:
        q.put( map(int, line.split()) )

Thread(target=producer, args=[SOME_INPUT_FILE_OR_SOMETHING]).start()
for i in range(10):
    Thread(target=consumer).start()

Kamaelia ist ein Python -Rahmen für den Aufbau von Anwendungen mit vielen Kommunikationsprozessen.

(Quelle: kamaelia.org) Kamaelia - Parallelität nützlich, Spaß

In Katamaelia bauen Sie Systeme aus einfache Komponenten, die miteinander sprechen. Dies beschleunigt die Entwicklung, hilft massiv die Wartung und bedeutet auch, dass Sie Bauen Sie natürlich gleichzeitige Software auf. Es ist beabsichtigt, durch zugänglich zu sein durch irgendein Entwickler, einschließlich Anfänger. Es macht es auch Spaß :)

Welche Art von Systemen? Netzwerkserver, Clients, Desktop -Anwendungen, Pygame -basierte Spiele, Transcode -Systeme und -Pipelines, digitale TV -Systeme, Spam -Eradikatoren, Lehrtools und ein angemessener Betrag mehr :)

Hier ist ein Video von Pycon 2009. Es beginnt mit dem Vergleich von Kamaelia mit Verdrehte und Parallele Python und gibt dann die Demonstration von Kamaelia.

Einfache Parallelität mit Kamaelia - Teil 1 (59:08)
Einfache Parallelität mit Kamaelia - Teil 2 (18:15)

In Bezug auf Kamaelien deckt die obige Antwort den Nutzen hier nicht wirklich ab. Der Kamaelien -Ansatz bietet eine einheitliche Schnittstelle, die pragmatisch nicht perfekt ist, um mit Threads, Generatoren und Prozessen in einem einzigen System für die Parallelität umzugehen.

Grundsätzlich bietet es eine Metapher für eine laufende Sache, die Posteingänge und Operationsboxen hat. Sie senden Nachrichten an Operationen und wenn Sie zusammen verkabelt werden, fließen Nachrichten von Operationskächern zu Posteingängen. Diese Metapher/API bleibt gleich, ob Sie Generatoren, Threads oder Prozesse verwenden oder mit anderen Systemen sprechen.

Der "nicht perfekte" Teil ist darauf zurückzuführen, dass syntaktische Zucker für Posteingänge und Operationen (obwohl dies zu diskutieren wird) noch nicht hinzugefügt wird.

Nehmen Sie das Beispiel für den Produzentenverbraucher mit dem oben genannten Fadenzusatz und wird dies in Kamaelien:

Pipeline(Producer(), Consumer() )

In diesem Beispiel spielt es keine Rolle, ob es sich um Gewindekomponenten handelt oder auf andere Weise, der einzige Unterschied zwischen ihnen ist die Basisklasse für die Komponente. Generatorkomponenten kommunizieren mithilfe von Listen, Thread -Komponenten mithilfe von Warteschlangen.queueis und prozessbasiert mit OS.Pipes.

Der Grund für diesen Ansatz ist jedoch, es schwieriger zu machen, Fehler zu debuggen. Bei Threading - oder einer gemeinsamen Speicherspeicherung, die Sie haben, ist das Problem, mit dem Sie konfrontiert sind, versehentlich mit gemeinsam genutzten Datenaktualisierungen unterbrochen. Durch die Verwendung von Nachrichtenübergaben eliminieren Sie eines Klasse von Bugs.

Wenn Sie nackte Threading und Sperren überall verwenden, wo Sie im Allgemeinen an der Annahme arbeiten, dass Sie beim Schreiben von Code keine Fehler machen. Während wir alle danach streben, ist es sehr selten, dass das passieren wird. Indem Sie das Verriegelungsverhalten an einem Ort abschließen, vereinfachen Sie, wo Dinge schief gehen können. (Kontext -Handler helfen, helfen jedoch nicht bei versehentlichen Updates außerhalb des Kontexthandlers)

Offensichtlich kann nicht jedes Code -Stück als Nachrichtenüberwachung und gemeinsamer Stil geschrieben werden. Deshalb hat Kamaelia auch einen einfachen Software -Transaktionsspeicher (STM), was eine wirklich nette Idee mit einem bösen Namen ist - es ähnelt eher der Versionskontrolle für Variablen - dh Schauen Sie sich einige Variablen an, aktualisieren Sie sie und verpflichten Sie sich zurück. Wenn Sie einen Zusammenstoß bekommen, spülen Sie und wiederholen Sie.

Relevante Links:

Wie auch immer, ich hoffe das ist eine nützliche Antwort. FWIW, der zentrale Grund für das Setup von Kamaelia besteht darin, die Parallelität sicherer und leichter in Python -Systemen zu verwenden, ohne dass der Schwanz mit dem Hund wedelt. (dh der große Eimer der Komponenten

Ich kann verstehen, warum die andere Kamaelia -Antwort niedergeschlagen wurde, da es für mich selbst eher wie eine Anzeige als wie eine Antwort aussieht. Als Autor von Kamaelia ist es schön, Begeisterung zu erkennen, obwohl ich hoffe, dass dies ein bisschen relevantere Inhalte enthält :-)

Und das ist meine Art zu sagen: Bitte nehmen Sie die Einschränkung, dass diese Antwort per Definition voreingenommen ist, aber für mich ist das Ziel von Kamaelia, zu versuchen, das zu wickeln, was IMO -Best Practice ist. Ich würde vorschlagen, ein paar Systeme auszuprobieren und zu sehen, was für Sie funktioniert. (Auch wenn dies für den Stack -Überlauf unangemessen ist, sorry - ich bin neu in diesem Forum :-)

Ich würde die Mikrotheads (Tasklets) von Stackless Python verwenden, wenn ich überhaupt Threads verwenden müsste.

Ein ganzes Online -Spiel (massiv Multiplayer) basiert auf stackless und sein Multithreading -Prinzip - da das Original nur für die massive Multiplayer -Eigenschaft des Spiels langsamer ist.

Themen in CPython sind weit verbreitet. Ein Grund ist der GIL - ein globales Dolmetscherschloss -, das das Fäden für viele Teile der Ausführung serialisiert. Meine Erfahrung ist, dass es wirklich schwierig ist, schnelle Anwendungen auf diese Weise zu erstellen. Meine Beispielkodierungen, bei denen alle mit einem Kern langsamer sind - mit einem Kern (aber viele Warten auf Eingaben sollten einige Leistungssteigerungen ermöglichen).

Verwenden Sie mit CPython eher separate Prozesse, wenn möglich.

Wenn Sie wirklich Ihre Hände schmutzig machen möchten, können Sie es versuchen Verwenden von Generatoren, um Coroutinen zu fälschen. Es ist wahrscheinlich nicht die effizienteste Arbeit, aber Coroutinen bieten Ihnen eine sehr gute Kontrolle über Kooperative Multitasking als vorbeugendes Multitasking werden Sie an anderer Stelle finden.

Ein Vorteil, den Sie finden, ist, dass Sie im Großen und Ganzen keine Schlösser oder Mutexes benötigen, wenn Sie kooperatives Multitasking verwenden, aber der wichtigere Vorteil für mich war die fast null-Schaltgeschwindigkeit zwischen "Threads". Natürlich soll Stackless Python auch dafür sehr gut sein; Und dann ist da noch Erlang, wenn dies nicht der Fall ist haben Python sein.

Der wahrscheinlich größte Nachteil beim kooperativen Multitasking ist der allgemeine Mangel an Problemumgehung für die Blockierung der I/O. Und in den gefälschten Coroutinen stoßen Sie auch auf das Problem, das Sie "Threads" nicht von etwas anderem als der oberen Ebene des Stapels innerhalb eines Threads wechseln können.

Nachdem Sie eine noch leicht komplexe Anwendung mit gefälschten Coroutinen gestellt haben, werden Sie die Arbeiten, die auf der OS -Ebene in die Prozessplanung gelangen, wirklich zu schätzen wissen.

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