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 .

3voto

amirfounder Punkte 11

Diese Frage ist schon ziemlich alt, aber ich wollte eine einfache Lösung vorstellen, die sich für mich bewährt hat und meinen Entwicklungsprozess unterstützt.

Die Methodik hinter dieser Antwort ist die Tatsache, dass die "neue" Zielfunktion, inner ist die Zuweisung des Ergebnisses der ursprünglichen Funktion (übergeben durch die __init__ Funktion) zum result Instanzattribut des Wrappers durch etwas, das Closure genannt wird.

Dadurch kann die Wrapper-Klasse den Rückgabewert aufbewahren, auf den der Aufrufer jederzeit zugreifen kann.

HINWEIS: Diese Methode muss keine manipulierten Methoden oder privaten Methoden der threading.Thread Klasse, obwohl Ertragsfunktionen nicht berücksichtigt wurden (OP erwähnte keine Ertragsfunktionen).

Viel Spaß!

from threading import Thread as _Thread

class ThreadWrapper:
    def __init__(self, target, *args, **kwargs):
        self.result = None
        self._target = self._build_threaded_fn(target)
        self.thread = _Thread(
            target=self._target,
            *args,
            **kwargs
        )

    def _build_threaded_fn(self, func):
        def inner(*args, **kwargs):
            self.result = func(*args, **kwargs)
        return inner

Zusätzlich können Sie pytest (vorausgesetzt, Sie haben es installiert) mit dem folgenden Code, um die Ergebnisse zu demonstrieren:

import time
from commons import ThreadWrapper

def test():

    def target():
        time.sleep(1)
        return 'Hello'

    wrapper = ThreadWrapper(target=target)
    wrapper.thread.start()

    r = wrapper.result
    assert r is None

    time.sleep(2)

    r = wrapper.result
    assert r == 'Hello'

2voto

pandy.song Punkte 31

Die Idee von GuySoft ist großartig, aber ich denke, dass das Objekt nicht unbedingt von Thread erben muss und start() könnte aus der Schnittstelle entfernt werden:

from threading import Thread
import queue
class ThreadWithReturnValue(object):
    def __init__(self, target=None, args=(), **kwargs):
        self._que = queue.Queue()
        self._t = Thread(target=lambda q,arg1,kwargs1: q.put(target(*arg1, **kwargs1)) ,
                args=(self._que, args, kwargs), )
        self._t.start()

    def join(self):
        self._t.join()
        return self._que.get()

def foo(bar):
    print('hello {0}'.format(bar))
    return "foo"

twrv = ThreadWithReturnValue(target=foo, args=('world!',))

print(twrv.join())   # prints foo

2voto

tscizzle Punkte 9693

Definieren Sie Ihr Ziel auf
1) ein Argument nehmen q
2) ersetzen Sie alle Aussagen return foo con q.put(foo); return

also eine Funktion

def func(a):
    ans = a * a
    return ans

werden würde

def func(a, q):
    ans = a * a
    q.put(ans)
    return

und dann würden Sie folgendermaßen vorgehen

from Queue import Queue
from threading import Thread

ans_q = Queue()
arg_tups = [(i, ans_q) for i in xrange(10)]

threads = [Thread(target=func, args=arg_tup) for arg_tup in arg_tups]
_ = [t.start() for t in threads]
_ = [t.join() for t in threads]
results = [q.get() for _ in xrange(len(threads))]

Und Sie können Funktionsdekoratoren/-wrapper verwenden, damit Sie Ihre bestehenden Funktionen als target ohne sie zu ändern, sondern nach diesem Grundschema.

1voto

Yves Dorfsman Punkte 2186

Wie bereits erwähnt, ist der Multiprocessing-Pool viel langsamer als das einfache Threading. Die Verwendung von Warteschlangen, wie sie in einigen Antworten hier vorgeschlagen wurden, ist eine sehr effektive Alternative. Ich habe sie mit Wörterbüchern verwendet, um viele kleine Threads laufen zu lassen und mehrere Antworten durch die Kombination mit Wörterbüchern wiederherzustellen:

#!/usr/bin/env python3

import threading
# use Queue for python2
import queue
import random

LETTERS = 'abcdefghijklmnopqrstuvwxyz'
LETTERS = [ x for x in LETTERS ]

NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

def randoms(k, q):
    result = dict()
    result['letter'] = random.choice(LETTERS)
    result['number'] = random.choice(NUMBERS)
    q.put({k: result})

threads = list()
q = queue.Queue()
results = dict()

for name in ('alpha', 'oscar', 'yankee',):
    threads.append( threading.Thread(target=randoms, args=(name, q)) )
    threads[-1].start()
_ = [ t.join() for t in threads ]
while not q.empty():
    results.update(q.get())

print(results)

1voto

ViperSniper0501 Punkte 181

Hier ist die von mir erstellte Version von @Kindall's Antwort .

In dieser Version müssen Sie nur noch Ihren Befehl mit Argumenten eingeben, um den neuen Thread zu erstellen.

Dies wurde mit Python 3.8 erstellt:

from threading import Thread
from typing import Any

def test(plug, plug2, plug3):
    print(f"hello {plug}")
    print(f'I am the second plug : {plug2}')
    print(plug3)
    return 'I am the return Value!'

def test2(msg):
    return f'I am from the second test: {msg}'

def test3():
    print('hello world')

def NewThread(com, Returning: bool, *arguments) -> Any:
    """
    Will create a new thread for a function/command.

    :param com: Command to be Executed
    :param arguments: Arguments to be sent to Command
    :param Returning: True/False Will this command need to return anything
    """
    class NewThreadWorker(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

    ntw = NewThreadWorker(target = com, args = (*arguments,))
    ntw.start()
    if Returning:
        return ntw.join()

if __name__ == "__main__":
    print(NewThread(test, True, 'hi', 'test', test2('hi')))
    NewThread(test3, True)

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