Der oben gezeigte “eine offensichtliche Weg, dies zu tun” funktioniert nicht: thread.join() ist zurückgekommen None.
irgendwie
Eine Möglichkeit, die ich gesehen habe, besteht darin, ein änderbares Objekt, z. B. 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 Slot in diesem Objekt speichern. Zum Beispiel:
def foo(bar, result, index):
print 'hello {0}'.format(bar)
result[index] = "foo"
from threading import Thread
threads = [None] * 10
results = [None] * 10
for i in range(len(threads)):
threads[i] = Thread(target=foo, args=('world!', results, i))
threads[i].start()
# do some other stuff
for i in range(len(threads)):
threads[i].join()
print " ".join(results) # what sound does a metasyntactic locomotive make?
Wenn du wirklich willst join() Um den Rückgabewert der aufgerufenen Funktion zurückzugeben, können Sie dies mit a tun Thread Unterklasse wie folgt:
from threading import Thread
def foo(bar):
print 'hello {0}'.format(bar)
return "foo"
class ThreadWithReturnValue(Thread):
def __init__(self, group=None, target=None, name=None,
args=(), kwargs={}, Verbose=None):
Thread.__init__(self, group, target, name, args, kwargs, Verbose)
self._return = None
def run(self):
if self._Thread__target is not None:
self._return = self._Thread__target(*self._Thread__args,
**self._Thread__kwargs)
def join(self):
Thread.join(self)
return self._return
twrv = ThreadWithReturnValue(target=foo, args=('world!',))
twrv.start()
print twrv.join() # prints foo
Das wird wegen einiger Namensverstümmelungen etwas haarig und greift auf “private” Datenstrukturen zu, die spezifisch für sind Thread Umsetzung … aber es funktioniert.
Für Python3
class ThreadWithReturnValue(Thread):
def __init__(self, group=None, target=None, name=None,
args=(), kwargs={}, Verbose=None):
Thread.__init__(self, group, target, name, args, kwargs)
self._return = None
def run(self):
print(type(self._target))
if self._target is not None:
self._return = self._target(*self._args,
**self._kwargs)
def join(self, *args):
Thread.join(self, *args)
return self._return
cool, danke für das beispiel! Ich frage mich, warum Thread nicht mit der Handhabung eines Rückgabewerts implementiert wurde, es scheint eine offensichtliche Sache zu sein, die unterstützt werden sollte.
– Wim
3. August 2011 um 1:28 Uhr
Ich denke, dies sollte die akzeptierte Antwort sein – das OP hat danach gefragt threadingkeine andere Bibliothek zum Ausprobieren, und die Beschränkung der Poolgröße führt zu einem zusätzlichen potenziellen Problem, das in meinem Fall aufgetreten ist.
– domoarigato
31. Januar 2015 um 20:05 Uhr
Auf python3 kehrt dies zurück TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given . Irgendeine Möglichkeit, das zu beheben?
– GuySoft
30. Oktober 2016 um 15:20 Uhr
join hat einen Timeout-Parameter, der weitergegeben werden sollte
– Teivaz
22. August 2018 um 10:44 Uhr
Warnung für jeden, der versucht ist, das zweite davon zu tun (die _Thread__target Ding). Sie werden jeden, der versucht, Ihren Code auf Python 3 zu portieren, dazu bringen, Sie zu hassen, bis er herausfindet, was Sie getan haben (weil er undokumentierte Funktionen verwendet, die sich zwischen 2 und 3 geändert haben). Dokumentieren Sie Ihren Code gut.
– Ben Taylor
20. November 2019 um 9:06 Uhr
Jake Biesinger
FWIW, die multiprocessing Modul hat dafür eine nette Schnittstelle mit dem Pool Klasse. Und wenn Sie lieber bei Threads als bei Prozessen bleiben möchten, können Sie einfach die verwenden multiprocessing.pool.ThreadPool Klasse als Drop-in-Ersatz.
def foo(bar, baz):
print 'hello {0}'.format(bar)
return 'foo' + baz
from multiprocessing.pool import ThreadPool
pool = ThreadPool(processes=1)
async_result = pool.apply_async(foo, ('world', 'foo')) # tuple of args for foo
# do some other stuff in the main process
return_val = async_result.get() # get the return value from your function.
@JakeBiesinger Mein Punkt ist, dass ich nach einer Antwort gesucht habe, wie ich eine Antwort vom Thread erhalten kann, hierher gekommen bin und die akzeptierte Antwort die angegebene Frage nicht beantwortet. Ich unterscheide Threads und Prozesse. Ich weiß über Global Interpreter Lock Bescheid, aber ich arbeite an einem E/A-gebundenen Problem, also sind Threads in Ordnung, ich brauche keine Prozesse. Andere Antworten hier beantworten die angegebene Frage besser.
– omikron
19. April 2015 um 9:17 Uhr
@omikron Aber Threads in Python geben keine Antwort zurück, es sei denn, Sie verwenden eine Unterklasse, die diese Funktionalität ermöglicht. Von möglichen Unterklassen sind ThreadPools eine gute Wahl (wählen Sie die Anzahl der Threads, verwenden Sie map/apply w/sync/async). Trotz Import aus multiprocesssie haben nichts mit Prozessen zu tun.
– Jake Biesinger
20. April 2015 um 2:17 Uhr
@JakeBiesinger Oh, ich bin blind. Sorry für meine unnötigen Kommentare. Sie haben Recht. Ich bin einfach davon ausgegangen, dass Multiprocessing = Prozesse.
– omikron
20. April 2015 um 9:25 Uhr
Einstellen nicht vergessen processes=1 zu mehr als einem, wenn Sie mehr Threads haben!
– Iman
16. Juni 2015 um 11:46 Uhr
Das Problem mit Multiprocessing und dem Thread-Pool besteht darin, dass das Einrichten und Starten von Threads im Vergleich zur grundlegenden Threading-Bibliothek viel langsamer ist. Es eignet sich hervorragend zum Starten lang laufender Threads, verfehlt jedoch den Zweck, wenn viele kurz laufende Threads gestartet werden müssen. Die in anderen Antworten hier dokumentierte Lösung zur Verwendung von “Threading” und “Queue” ist meiner Meinung nach eine bessere Alternative für diesen letzteren Anwendungsfall.
– Yves Dorfsmann
8. Januar 2018 um 14:41 Uhr
Ramarao Amara
In Python 3.2+ ist stdlib concurrent.futures Modul bietet eine API auf höherer Ebene threadingeinschließlich der Übergabe von Rückgabewerten oder Ausnahmen von einem Worker-Thread zurück an den Haupt-Thread:
import concurrent.futures
def foo(bar):
print('hello {}'.format(bar))
return 'foo'
with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(foo, 'world!')
return_value = future.result()
print(return_value)
Für diejenigen, die sich fragen, kann dies mit einer Liste von Threads durchgeführt werden. futures = [executor.submit(foo, param) for param in param_list] Die Bestellung wird beibehalten, und das Verlassen der with ermöglicht die Ergebniserfassung. [f.result() for f in futures]
– jayreed1
4. Juni 2020 um 21:29 Uhr
@ jayreed1 dieser Kommentar verdient eine eigene Antwort oder sollte in die Antwort aufgenommen werden. Sehr hilfreich.
– Damien
5. August 2020 um 10:39 Uhr
Wow … danke für die Antwort, ich habe nach einer Multiprocessing-Lösung für meinen Code gesucht, aber das hilft mir, es auf so einfache Weise zu tun, und der Kommentar von @jayreed1 hat es auf den Kuchen gebracht, danke an alle …
– DevPy
27. April 2021 um 13:22 Uhr
Vielen Dank, dies hat mir geholfen, ein Problem zu beheben, das ich in einigen nicht-threadsicheren Bibliotheken gefunden habe. Ich mochte Ihre Antwort von dort. Meine Fragen und Antworten: stackoverflow.com/questions/68982519/…
– xCovelus
30. August 2021 um 10:50 Uhr
Ich habe noch nie mit dieser Bibliothek gearbeitet. Muss ich den Thread irgendwie schließen, damit er nicht “herumhängt”, oder erledigt das der Ausführende automatisch für mich, wenn ich nur den hier gezeigten Code verwende?
– Rumi P.
22. September 2021 um 11:41 Uhr
bj0
Jakes Antwort ist gut, aber wenn Sie keinen Threadpool verwenden möchten (Sie wissen nicht, wie viele Threads Sie benötigen, aber erstellen Sie sie nach Bedarf), dann ist das integrierte eine gute Möglichkeit, Informationen zwischen Threads zu übertragen Warteschlange.Warteschlange Klasse, da es Fadensicherheit bietet.
Ich habe den folgenden Decorator erstellt, damit er sich ähnlich wie der Threadpool verhält:
def threaded(f, daemon=False):
import Queue
def wrapped_f(q, *args, **kwargs):
'''this function calls the decorated function and puts the
result in a queue'''
ret = f(*args, **kwargs)
q.put(ret)
def wrap(*args, **kwargs):
'''this is the function returned from the decorator. It fires off
wrapped_f in a new thread and returns the thread object with
the result queue attached'''
q = Queue.Queue()
t = threading.Thread(target=wrapped_f, args=(q,)+args, kwargs=kwargs)
t.daemon = daemon
t.start()
t.result_queue = q
return t
return wrap
Dann verwenden Sie es einfach als:
@threaded
def long_task(x):
import time
x = x + 5
time.sleep(5)
return x
# does not block, returns Thread object
y = long_task(10)
print y
# this blocks, waiting for the result
result = y.result_queue.get()
print result
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, seit ich diese Antwort gepostet habe, aber sie wird immer noch angesehen, also dachte ich, ich würde sie aktualisieren, um die Art und Weise widerzuspiegeln, wie ich dies in neueren Versionen von Python mache:
Python 3.2 hinzugefügt im concurrent.futures Modul, das eine High-Level-Schnittstelle für parallele Aufgaben bereitstellt. Es bietet ThreadPoolExecutor und ProcessPoolExecutorsodass Sie einen Thread oder Prozesspool mit derselben API verwenden können.
Ein Vorteil dieser API besteht darin, dass das Senden einer Aufgabe an eine Executor gibt a zurück Future -Objekt, das mit dem Rückgabewert des von Ihnen übermittelten Callable vervollständigt wird.
Dadurch wird das Anbringen a queue Objekt unnötig, was den Decorator ziemlich vereinfacht:
Dies wird eine Standardeinstellung verwenden Modul Threadpool-Executor, wenn keiner übergeben wird.
Die Verwendung ist sehr ähnlich wie zuvor:
@threadpool
def long_task(x):
import time
x = x + 5
time.sleep(5)
return x
# does not block, returns Future object
y = long_task(10)
print y
# this blocks, waiting for the result
result = y.result()
print result
Wenn Sie Python 3.4+ verwenden, besteht ein wirklich nettes Feature bei der Verwendung dieser Methode (und von Future-Objekten im Allgemeinen) darin, dass die zurückgegebene Zukunft umschlossen werden kann, um sie in eine umzuwandeln asyncio.Future mit asyncio.wrap_future. Dadurch funktioniert es problemlos mit Coroutinen:
result = await asyncio.wrap_future(long_task(10))
Wenn Sie keinen Zugriff auf den Basiswert benötigen concurrent.Future -Objekt können Sie den Wrap in den Decorator einfügen:
Wann immer Sie dann CPU-intensiven oder blockierenden Code aus dem Event-Loop-Thread verschieben müssen, können Sie ihn in eine dekorierte Funktion einfügen:
@threadpool
def some_long_calculation():
...
# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()
Arik
Eine andere Lösung, die keine Änderung Ihres vorhandenen Codes erfordert:
Es kann auch leicht an eine Multithread-Umgebung angepasst werden:
import Queue # Python 2.x
#from queue import Queue # Python 3.x
from threading import Thread
def foo(bar):
print 'hello {0}'.format(bar) # Python 2.x
#print('hello {0}'.format(bar)) # Python 3.x
return 'foo'
que = Queue.Queue() # Python 2.x
#que = Queue() # Python 3.x
threads_list = list()
t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
threads_list.append
# Add more threads here
...
threads_list.append(t2)
...
threads_list.append(t3)
...
# Join all the threads
for t in threads_list:
t.join()
# Check thread's return value
while not que.empty():
result = que.get()
print result # Python 2.x
#print(result) # Python 3.x
t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, ‘world!’)) was macht q.put hier, was macht die Queue.Queue()
– Vijay Shanker
29. Oktober 2016 um 21:54 Uhr
que = Queue.Queue() – erstellt eine Warteschlange q.put(foo) – fügt foo() in die Warteschlange ein
– Arik
5. November 2016 um 11:09 Uhr
Für Python3 müssen Sie zu ändern from queue import Queue.
– Gino Mempin
6. Februar 2019 um 7:25 Uhr
Dies scheint die am wenigsten störende Methode zu sein (keine Notwendigkeit, die ursprüngliche Codebasis dramatisch umzustrukturieren), damit der Rückgabewert zum Hauptthread zurückkehrt.
– Fanchen Bao
17. Dezember 2019 um 23:07 Uhr
@DaniyalWarraich Ich habe gerade beide Beispiele mit Python 3 ausgeführt und beide funktionieren wie ein Zauber. Stellen Sie sicher, dass Sie die relevanten Zeilen auskommentieren/entkommentieren.
– Arik
9. Januar um 13:02 Uhr
Die meisten Antworten, die ich gefunden habe, sind lang und erfordern die Vertrautheit mit anderen Modulen oder erweiterten Python-Funktionen und werden für jemanden ziemlich verwirrend sein, es sei denn, sie sind bereits mit allem vertraut, worüber die Antwort spricht.
import time, random
def function_to_thread(n):
count = 0
while count < 3:
print(f'still running thread {n}')
count +=1
time.sleep(3)
result = random.random()
print(f'Return value of thread {n} should be: {result}')
return result
def main():
thread1 = ThreadWithResult(target=function_to_thread, args=(1,))
thread2 = ThreadWithResult(target=function_to_thread, args=(2,))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(thread1.result)
print(thread2.result)
main()
Erläuterung:
Ich wollte die Dinge erheblich vereinfachen, also habe ich eine erstellt ThreadWithResult Klasse und hatte es erben von threading.Thread. Die verschachtelte Funktion function in __init__ ruft die Thread-Funktion auf, deren Wert wir speichern möchten, und speichert das Ergebnis dieser verschachtelten Funktion als Instanzattribut self.result nachdem der Thread die Ausführung beendet hat.
Das Erstellen einer Instanz von this ist identisch mit dem Erstellen einer Instanz von threading.Thread. Übergeben Sie die Funktion, die Sie in einem neuen Thread ausführen möchten, an die target Argument und alle Argumente, die Ihre Funktion möglicherweise benötigt args -Argument und alle Schlüsselwortargumente für die kwargs Streit.
Ich denke, dies ist wesentlich einfacher zu verstehen als die überwiegende Mehrheit der Antworten, und dieser Ansatz erfordert keine zusätzlichen Importe! Ich habe die eingeschlossen time und random Modul, um das Verhalten eines Threads zu simulieren, aber sie sind nicht erforderlich, um die in der ursprünglichen Frage gestellte Funktionalität zu erreichen.
Ich weiß, ich beantworte diese Frage schon lange, nachdem die Frage gestellt wurde, aber ich hoffe, dass dies in Zukunft mehr Menschen helfen kann!
BEARBEITEN: Ich habe die erstellt save-thread-result PyPI-Paket damit Sie auf denselben Code oben zugreifen und ihn projektübergreifend wiederverwenden können (GitHub-Code ist hier). Das PyPI-Paket erweitert die threading.Thread Klasse, sodass Sie alle Attribute festlegen können, die Sie festlegen würden threading.thread auf der ThreadWithResult Klasse auch!
pip3 install -U save-thread-result # MacOS/Linux
pip install -U save-thread-result # Windows
python3 # MacOS/Linux
python # Windows
from save_thread_result import ThreadWithResult
# As of Release 0.0.3, you can also specify values for
#`group`, `name`, and `daemon` if you want to set those
# values manually.
thread = ThreadWithResult(
target = my_function,
args = (my_function_arg1, my_function_arg2, ...)
kwargs = {my_function_kwarg1: kwarg1_value, my_function_kwarg2: kwarg2_value, ...}
)
thread.start()
thread.join()
if getattr(thread, 'result', None):
print(thread.result)
else:
# thread.result attribute not set - something caused
# the thread to terminate BEFORE the thread finished
# executing the function passed in through the
# `target` argument
print('ERROR! Something went wrong while executing this thread, and the function you passed in did NOT complete!!')
# seeing help about the class and information about the threading.Thread super class methods and attributes available:
help(ThreadWithResult)
t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, ‘world!’)) was macht q.put hier, was macht die Queue.Queue()
– Vijay Shanker
29. Oktober 2016 um 21:54 Uhr
que = Queue.Queue() – erstellt eine Warteschlange q.put(foo) – fügt foo() in die Warteschlange ein
– Arik
5. November 2016 um 11:09 Uhr
Für Python3 müssen Sie zu ändern from queue import Queue.
– Gino Mempin
6. Februar 2019 um 7:25 Uhr
Dies scheint die am wenigsten störende Methode zu sein (keine Notwendigkeit, die ursprüngliche Codebasis dramatisch umzustrukturieren), damit der Rückgabewert zum Hauptthread zurückkehrt.
– Fanchen Bao
17. Dezember 2019 um 23:07 Uhr
@DaniyalWarraich Ich habe gerade beide Beispiele mit Python 3 ausgeführt und beide funktionieren wie ein Zauber. Stellen Sie sicher, dass Sie die relevanten Zeilen auskommentieren/entkommentieren.
– Arik
9. Januar um 13:02 Uhr
Gemeinschaft
Parris / Kindalls Antwort join/return Antwort auf Python 3 portiert:
from threading import Thread
def foo(bar):
print('hello {0}'.format(bar))
return "foo"
class ThreadWithReturnValue(Thread):
def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
Thread.__init__(self, group, target, name, args, kwargs, daemon=daemon)
self._return = None
def run(self):
if self._target is not None:
self._return = self._target(*self._args, **self._kwargs)
def join(self):
Thread.join(self)
return self._return
twrv = ThreadWithReturnValue(target=foo, args=('world!',))
twrv.start()
print(twrv.join()) # prints foo
Beachten Sie das Thread -Klasse ist in Python 3 anders implementiert.
join akzeptiert einen timeout-Parameter, der weitergegeben werden sollte