Wie erhalte ich den Rückgabewert von einem Thread?

Lesezeit: 15 Minuten

Benutzeravatar von wim
Wim

Die Funktion foo unten gibt eine Zeichenfolge zurück 'foo'. Wie bekomme ich den Wert 'foo' die 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() ist zurückgekommen None.

kindalls Benutzeravatar
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):
        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

Benutzeravatar von Jake Biesinger
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

Benutzeravatar von Ramarao Amara
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

Benutzeravatar von bj0
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 ganze Weile her, dass 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:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return (executor or _DEFAULT_POOL).submit(f, *args, **kwargs)

    return wrap

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:

_DEFAULT_POOL = ThreadPoolExecutor()

def threadpool(f, executor=None):
    @wraps(f)
    def wrap(*args, **kwargs):
        return asyncio.wrap_future((executor or _DEFAULT_POOL).submit(f, *args, **kwargs))

    return wrap

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()

Ariks Benutzeravatar
Arik

Eine andere Lösung, die keine Änderung Ihres vorhandenen Codes erfordert:

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

t = Thread(target=lambda q, arg1: q.put(foo(arg1)), args=(que, 'world!'))
t.start()
t.join()
result = que.get()
print result             # Python 2.x
#print(result)           # Python 3.x

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

  • 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

  • @Jawad Das Lambda ist das Ziel des Threads. Lambda hat 2 Argumente: q und arg1. Dann übergeben wir zwei Argumente an Lambda: args=(que, 'world!') Schließlich ist arg1 die Eingabe für die Funktion foo(), world1 in diesem Beispiel.

    – Arik

    6. Mai um 15:24 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.

Arbeitscode für einen vereinfachten Ansatz:

import threading

class ThreadWithResult(threading.Thread):
    def __init__(self, group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None):
        def function():
            self.result = target(*args, **kwargs)
        super().__init__(group=group, target=function, name=name, daemon=daemon)

Beispielcode:

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.

z.B

my_thread = ThreadWithResult(target=my_function, args=(arg1, arg2, arg3))

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!

Die ursprüngliche Antwort oben geht auf die Hauptidee hinter dieser Unterklasse ein, aber für weitere Informationen siehe die genauere Erklärung (aus dem Modul docstring) hier.

Schnelles Anwendungsbeispiel:

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

  • 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

  • @Jawad Das Lambda ist das Ziel des Threads. Lambda hat 2 Argumente: q und arg1. Dann übergeben wir zwei Argumente an Lambda: args=(que, 'world!') Schließlich ist arg1 die Eingabe für die Funktion foo(), world1 in diesem Beispiel.

    – Arik

    6. Mai um 15:24 Uhr


Benutzeravatar der Community
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

    – cz

    5. Januar 2017 um 11:57 Uhr

  • Die Dokumentation besagt, dass die einzigen zu überschreibenden Methoden sein sollten: __init__() und run() docs.python.org/3/library/threading.html#thread-objects

    – lmiguelmh

    6. August 2021 um 19:10 Uhr


  • @lmiguelmh, Dies ist eine Fortsetzung einer früheren Antwort, die erklärt, dass ja, dies mit Interna durcheinander kommt.

    – Ikegami

    26. Oktober um 15:19 Uhr

1436540cookie-checkWie erhalte ich den Rückgabewert von einem Thread?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy