Die folgende Funktion foo
gibt eine Zeichenfolge zurück 'foo'
. Wie kann ich den Wert erhalten, 'foo'
der vom Ziel des Threads zurückgegeben wird?
from threading import Thread
def foo(bar):
print('hello {}'.format(bar))
return 'foo'
thread = Thread(target=foo, args=('world!',))
thread.start()
return_value = thread.join()
Der oben gezeigte "eine offensichtliche Weg, dies zu tun" funktioniert nicht: thread.join()
zurückgegeben None
.
futures = [executor.submit(foo, param) for param in param_list]
Die Reihenfolge wird beibehalten, und das Verlassen der Reihenfolgewith
ermöglicht die Ergebniserfassung.[f.result() for f in futures]
FWIW, das
multiprocessing
Modul hat dafür eine schöne Schnittstelle mit derPool
Klasse. Und wenn Sie sich eher an Threads als an Prozesse halten möchten, können Sie diemultiprocessing.pool.ThreadPool
Klasse einfach als Drop-In-Ersatz verwenden.quelle
multiprocess
, haben sie nichts mit Prozessen zu tun.processes=1
mehr als einen zu setzen , wenn Sie mehr Threads haben!Eine Möglichkeit, die ich gesehen habe, besteht darin, ein veränderbares Objekt wie eine Liste oder ein Wörterbuch zusammen mit einem Index oder einer anderen Kennung an den Konstruktor des Threads zu übergeben. Der Thread kann dann seine Ergebnisse in seinem dedizierten Steckplatz in diesem Objekt speichern. Zum Beispiel:
Wenn Sie
join()
den Rückgabewert der aufgerufenen Funktion wirklich zurückgeben möchten , können Sie dies mit einerThread
Unterklasse wie der folgenden tun :Das wird ein wenig haarig, weil ein Name verwirrt wird, und es greift auf "private" Datenstrukturen zu, die für die
Thread
Implementierung spezifisch sind ... aber es funktioniert.Für Python3
quelle
threading
, keine andere Bibliothek zu versuchen, und die Begrenzung der Poolgröße führt zu einem zusätzlichen potenziellen Problem, das in meinem Fall aufgetreten ist.TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given
. Wie kann man das beheben?_Thread__target
Ding) zu tun . Sie werden jeden, der versucht, Ihren Code auf Python 3 zu portieren, dazu bringen, Sie zu hassen, bis er herausgefunden hat, was Sie getan haben (aufgrund der Verwendung von undokumentierten Funktionen, die sich zwischen 2 und 3 geändert haben). Dokumentieren Sie Ihren Code gut.Jakes Antwort ist gut, aber wenn Sie keinen Threadpool verwenden möchten (Sie wissen nicht, wie viele Threads Sie benötigen, sondern sie nach Bedarf erstellen), ist die integrierte Methode eine gute Möglichkeit, Informationen zwischen Threads zu übertragen Queue.Queue- Klasse, da sie Thread-Sicherheit bietet.
Ich habe den folgenden Dekorator erstellt, damit er sich ähnlich wie der Threadpool verhält:
Dann verwenden Sie es einfach als:
Die dekorierte Funktion erstellt bei jedem Aufruf einen neuen Thread und gibt ein Thread-Objekt zurück, das die Warteschlange enthält, die das Ergebnis empfängt.
AKTUALISIEREN
Es ist schon eine Weile her, dass ich diese Antwort gepostet habe, aber es werden immer noch Ansichten angezeigt, sodass ich dachte, ich würde sie aktualisieren, um die Art und Weise widerzuspiegeln, wie ich dies in neueren Versionen von Python mache:
Python 3.2 wurde in das
concurrent.futures
Modul aufgenommen, das eine allgemeine Schnittstelle für parallele Aufgaben bietet. Es bietetThreadPoolExecutor
undProcessPoolExecutor
, so können Sie einen Thread oder Prozesspool mit derselben API verwenden.Ein Vorteil dieser api ist , dass eine Aufgabe zu einer Vorlage
Executor
kehrt einFuture
Objekt, das mit dem Rückgabewert der abrufbaren vervollständigen Sie einreichen.Dies macht das Anbringen eines
queue
Objekts unnötig, was den Dekorateur erheblich vereinfacht:Dies verwendet einen Standard- Modul- Threadpool-Executor, wenn einer nicht übergeben wird.
Die Verwendung ist sehr ähnlich wie zuvor:
Wenn Sie Python 3.4+ verwenden, ist eine wirklich nette Funktion bei der Verwendung dieser Methode (und zukünftiger Objekte im Allgemeinen), dass die zurückgegebene Zukunft umbrochen werden kann, um sie in eine
asyncio.Future
mit zu verwandelnasyncio.wrap_future
. Dies macht es einfach mit Coroutinen zu arbeiten:Wenn Sie keinen Zugriff auf das zugrunde liegende
concurrent.Future
Objekt benötigen , können Sie den Wrap in den Dekorator aufnehmen:Wenn Sie dann den CPU-intensiven oder blockierenden Code aus dem Ereignisschleifen-Thread entfernen müssen, können Sie ihn in eine dekorierte Funktion einfügen:
quelle
AttributeError: 'module' object has no attribute 'Lock'
dies anscheinend von der Zeile ausgehty = long_task(10)
... Gedanken?Eine weitere Lösung, bei der Sie Ihren vorhandenen Code nicht ändern müssen:
Es kann auch leicht an eine Multithread-Umgebung angepasst werden:
quelle
from queue import Queue
.Parris / kindalls Antwort
join
/return
Antwort auf Python 3 portiert:Beachten Sie, dass die
Thread
Klasse in Python 3 anders implementiert ist.quelle
Ich habe die Antwort von kindall gestohlen und sie nur ein wenig aufgeräumt.
Der Schlüsselteil ist das Hinzufügen von * args und ** kwargs zu join (), um das Timeout zu behandeln
AKTUALISIERTE ANTWORT UNTEN
Dies ist meine am häufigsten gewählte Antwort, daher habe ich beschlossen, mit Code zu aktualisieren, der sowohl auf py2 als auch auf py3 ausgeführt wird.
Außerdem sehe ich viele Antworten auf diese Frage, die ein Unverständnis in Bezug auf Thread.join () zeigen. Einige können das
timeout
Argument überhaupt nicht verarbeiten. Es gibt aber auch einen Eckfall, den Sie in Bezug auf Fälle kennen sollten, in denen Sie (1) eine Zielfunktion haben, die zurückkehren kann,None
und (2) Sie auch dastimeout
Argument an join () übergeben. Bitte lesen Sie "TEST 4", um diesen Eckfall zu verstehen.ThreadWithReturn-Klasse, die mit py2 und py3 funktioniert:
Einige Beispieltests sind unten gezeigt:
Können Sie den Eckfall identifizieren, auf den wir möglicherweise bei TEST 4 stoßen?
Das Problem ist, dass wir erwarten, dass giveMe () None zurückgibt (siehe TEST 2), aber wir erwarten auch, dass join () None zurückgibt, wenn das Zeitlimit überschritten wird.
returned is None
bedeutet entweder:(1) das hat giveMe () zurückgegeben, oder
(2) Zeitüberschreitung bei join ()
Dieses Beispiel ist trivial, da wir wissen, dass giveMe () immer None zurückgibt. In der realen Welt (in der das Ziel möglicherweise keine oder etwas anderes zurückgibt) möchten wir jedoch explizit überprüfen, was passiert ist.
Im Folgenden erfahren Sie, wie Sie diesen Eckfall angehen können:
quelle
target
, um sie als Mitgliedsvariablen in Ihrer Klasse zu initiieren .args
kwargs
Warteschlange verwenden:
quelle
out_queue1
Sie die Warteschlangeout_queue1.get()
durchlaufen und abfangen. Leere Ausnahme :ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass
. Semikolons zur Simulation von Zeilenumbrüchen.Meine Lösung für das Problem besteht darin, die Funktion und den Thread in eine Klasse einzubinden. Erfordert keine Pools, Warteschlangen oder Variablenübergabe vom Typ c. Es ist auch nicht blockierend. Sie überprüfen stattdessen den Status. Siehe Beispiel für die Verwendung am Ende des Codes.
quelle
join
Immer zurückkehrenNone
, ich denke, Sie sollten UnterklasseThread
, um Rückkehrcodes und so zu behandeln.quelle
Unter Berücksichtigung des @iman- Kommentars zur @ JakeBiesinger- Antwort habe ich es neu zusammengestellt, um eine unterschiedliche Anzahl von Threads zu haben:
Prost,
Kerl.
quelle
Sie können eine Variable über dem Bereich der Thread-Funktion definieren und das Ergebnis hinzufügen. (Ich habe den Code auch so geändert, dass er mit Python3 kompatibel ist.)
Dies kehrt zurück
{'world!': 'foo'}
Wenn Sie die Funktionseingabe als Schlüssel für Ihr Ergebnisdiktat verwenden, wird garantiert, dass jede einzelne Eingabe einen Eintrag in die Ergebnisse liefert
quelle
Ich verwende diesen Wrapper, der jede Funktion zum Ausführen in a bequem umdreht
Thread
und sich um ihren Rückgabewert oder ihre Ausnahme kümmert. Es fügt keinenQueue
Overhead hinzu.Anwendungsbeispiele
Hinweise zum
threading
ModulKomfortable Rückgabewerte und Ausnahmebehandlung einer Thread-Funktion sind ein häufiges "Pythonic" -Bedarf und sollten vom
threading
Modul bereits angeboten werden - möglicherweise direkt in der StandardklasseThread
.ThreadPool
hat viel zu viel Overhead für einfache Aufgaben - 3 Threads verwalten, viel Bürokratie. Leider wurdeThread
das Layout ursprünglich von Java kopiert - was Sie zB aus dem noch nutzlosen 1. (!) Konstruktorparameter sehengroup
.quelle
Definieren Sie Ihr Ziel auf
1) nehmen Sie ein Argument
q
2) ersetzen Sie alle Anweisungen
return foo
durchq.put(foo); return
also eine Funktion
würde werden
und dann würden Sie als solche fortfahren
Und Sie können Funktionsdekoratoren / Wrapper verwenden, um es so zu gestalten, dass Sie Ihre vorhandenen Funktionen verwenden können,
target
ohne sie zu ändern, aber folgen Sie diesem Grundschema.quelle
results = [ans_q.get() for _ in xrange(len(threads))]
Wie bereits erwähnt, ist der Multiprocessing-Pool viel langsamer als das grundlegende Threading. Die Verwendung von Warteschlangen, wie in einigen Antworten hier vorgeschlagen, ist eine sehr effektive Alternative. Ich habe es mit Wörterbüchern verwendet, um viele kleine Threads ausführen und mehrere Antworten wiederherstellen zu können, indem ich sie mit Wörterbüchern kombiniere:
quelle
Die Idee von GuySoft ist großartig, aber ich denke, das Objekt muss nicht unbedingt von Thread erben und start () könnte von der Schnittstelle entfernt werden:
quelle
Eine übliche Lösung besteht darin, Ihre Funktion
foo
mit einem Dekorateur wie zu verpackenDann sieht der ganze Code vielleicht so aus
Hinweis
Ein wichtiges Problem ist, dass die Rückgabewerte möglicherweise ungeordnet sind . (Tatsächlich wird das
return value
nicht unbedingt im gespeichertqueue
, da Sie eine beliebige thread-sichere Datenstruktur wählen können )quelle
Warum nicht einfach eine globale Variable verwenden?
quelle
Kindalls Antwort in Python3
quelle
Wenn nur True oder False aus dem Aufruf einer Funktion überprüft werden soll, besteht eine einfachere Lösung darin, eine globale Liste zu aktualisieren.
Dies ist hilfreicher, wenn Sie herausfinden möchten, ob einer der Threads einen falschen Status zurückgegeben hat, um die erforderlichen Maßnahmen zu ergreifen.
quelle