647 Stimmen

Wie erhält man den Rückgabewert eines Threads in Python?

Die Funktion foo unten gibt eine Zeichenkette zurück 'foo' . Wie kann ich den Wert erhalten '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()

Die oben gezeigte "offensichtliche Lösung" funktioniert nicht: thread.join() zurückgegeben None .

474voto

kindall Punkte 167554

Eine Möglichkeit, die ich gesehen habe, ist die Übergabe eines veränderbaren Objekts, z. B. einer Liste oder eines Wörterbuchs, an den Konstruktor des Threads, zusammen mit einem Index oder einer anderen Kennung irgendeiner Art. Der Thread kann dann seine Ergebnisse in seinem eigenen 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 Sie wirklich wollen join() den Rückgabewert der aufgerufenen Funktion zurückgeben möchten, können Sie dies mit einer Thread Unterklasse wie die folgende:

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 ein wenig haarig wegen einiger Namensverwechslungen, und es greift auf "private" Datenstrukturen zu, die spezifisch sind für 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

368voto

Jake Biesinger Punkte 5008

Übrigens, die multiprocessing Modul hat dafür eine schöne Schnittstelle mit der Pool Klasse. Und wenn Sie eher mit Threads als mit Prozessen arbeiten wollen, können Sie einfach die multiprocessing.pool.ThreadPool Klasse als Ersatz für einen Drop-in.

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.

311voto

Ramarao Amara Punkte 2718

In Python 3.2+, stdlib concurrent.futures Modul bietet eine API auf höherer Ebene für threading einschließlich der Weitergabe 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)

118voto

bj0 Punkte 7311

Jakes Antwort ist gut, aber wenn Sie keinen Threadpool verwenden wollen (Sie wissen nicht, wie viele Threads Sie benötigen, aber erstellen Sie sie nach Bedarf), dann ist eine gute Möglichkeit, Informationen zwischen Threads zu übertragen, die eingebaute Warteschlange.Warteschlange Klasse, da sie Thread-Sicherheit bietet.

Ich habe den folgenden Dekorator 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 erhalten wird.

UPDATE

Es ist schon eine ganze Weile her, seit ich diese Antwort gepostet habe, aber sie wird immer noch aufgerufen, also dachte ich, ich würde sie aktualisieren, um die Art und Weise wiederzugeben, wie ich das in neueren Versionen von Python mache:

Python 3.2 hinzugefügt in der concurrent.futures Modul, das eine High-Level-Schnittstelle für parallele Aufgaben bietet. Es bietet ThreadPoolExecutor y ProcessPoolExecutor Sie können also einen Thread oder einen Prozess-Pool mit der gleichen API verwenden.

Ein Vorteil dieser API ist, dass die Übermittlung einer Aufgabe an eine Executor gibt eine Future Objekt, das mit dem Rückgabewert der von Ihnen übermittelten Callable abgeschlossen wird.

Dies macht das Anbringen einer queue Objekt überflüssig, was den Dekorator erheblich 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

Dabei wird ein Standard Modul Threadpool-Executor, wenn kein solcher ü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, ist eine wirklich nette Funktion dieser Methode (und von Future-Objekten im Allgemeinen), dass die zurückgegebene Zukunft in eine "Wrapping"-Methode umgewandelt werden kann. asyncio.Future con asyncio.wrap_future . Dies erleichtert die Arbeit mit Koroutinen:

result = await asyncio.wrap_future(long_task(10))

Wenn Sie keinen Zugriff auf die zugrunde liegenden Daten benötigen concurrent.Future Objekts können Sie den Umbruch in den Dekorator aufnehmen:

_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

Wenn Sie dann einen rechenintensiven oder blockierenden Code aus dem Ereignisschleifen-Thread auslagern 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()

94voto

Arik Punkte 1119

Eine weitere Lösung, die keine Änderung des bestehenden 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 Multithreading-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(t)

# 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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X