Come ottenere il valore restituito da un thread in Python?


342

La funzione fooseguente restituisce una stringa 'foo'. Come posso ottenere il valore 'foo'che viene restituito dalla destinazione del thread?

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

"Un modo ovvio per farlo", mostrato sopra, non funziona: thread.join()restituito None.

Risposte:


38

In Python 3.2+, il concurrent.futuresmodulo stdlib fornisce un'API di livello superiore threading, incluso il passaggio di valori di ritorno o eccezioni da un thread di lavoro al thread principale:

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)

1
Per coloro che si chiedono questo può essere fatto con un elenco di thread. futures = [executor.submit(foo, param) for param in param_list]L'ordine verrà mantenuto e l'uscita da withconsentirà la raccolta dei risultati. [f.result() for f in futures]
jayreed1

273

FWIW, il multiprocessingmodulo ha una bella interfaccia per questo usando la Poolclasse. E se vuoi rimanere fedele ai thread anziché ai processi, puoi semplicemente usare la multiprocessing.pool.ThreadPoolclasse come rimpiazzo 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.

50
@JakeBiesinger Il mio punto è che stavo cercando una risposta, come ottenere la risposta da Thread, sono venuto qui e la risposta accettata non risponde alla domanda dichiarata. Differenzi i thread e i processi. Conosco il Global Interpreter Lock, tuttavia sto lavorando al problema legato all'I / O, quindi i thread sono ok, non ho bisogno di processi. Altre risposte qui meglio rispondere alla domanda dichiarata.
omikron,

7
@omikron Ma i thread in Python non restituiscono una risposta se non si utilizza una sottoclasse che abilita questa funzionalità. Di possibili sottoclassi, ThreadPools è un'ottima scelta (scegli il numero di thread, usa la mappa / applica w / sync / async). Nonostante siano importati da multiprocess, non hanno nulla a che fare con i processi.
Jake Biesinger,

4
@JakeBiesinger Oh, sono cieco. Ci scusiamo per i miei commenti non necessari. Hai ragione. Ho appena assunto che i processi multipli =.
omikron,

12
Non dimenticare di impostare processes=1più di uno se hai più thread!
iman,

4
Il problema con il multiprocessing e il pool di thread è che è molto più lento impostare e avviare i thread rispetto alla libreria di threading di base. È ottimo per avviare thread con esecuzione prolungata, ma sconfiggi lo scopo quando devi avviare molti thread con esecuzione breve. La soluzione dell'utilizzo di "threading" e "Queue" documentata in altre risposte qui è un'alternativa migliore per quest'ultimo caso d'uso secondo me.
Yves Dorfsman,

243

Un modo che ho visto è passare un oggetto mutabile, come un elenco o un dizionario, al costruttore del thread, insieme a un indice o un altro identificatore di qualche tipo. Il thread può quindi archiviare i risultati nel suo slot dedicato in quell'oggetto. Per esempio:

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?

Se si desidera veramente join()restituire il valore restituito della funzione chiamata, è possibile farlo con una Threadsottoclasse come la seguente:

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

Questo diventa un po 'peloso a causa di una manomissione del nome e accede a strutture di dati "private" specifiche Thread dell'implementazione ... ma funziona.

Per 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

37
fantastico, grazie per l'esempio! mi chiedo perché Thread non sia stato implementato con la gestione di un valore di ritorno in primo luogo, sembra una cosa abbastanza ovvia da supportare.
mercoledì

16
Penso che questa dovrebbe essere la risposta accettata: l'OP ha chiesto threading, non una libreria diversa da provare, inoltre la limitazione della dimensione del pool introduce un ulteriore potenziale problema, che si è verificato nel mio caso.
domoarigato,

10
Grande scherzo del treno.
meawoppl,

7
Su python3 questo ritorna TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given. Un modo per risolverlo?
GuySoft,

2
Avvertimento per chiunque sia tentato di fare il secondo di questi (la _Thread__targetcosa). Farai odiare chiunque provi a trasferire il tuo codice su Python 3 fino a quando non risolveranno ciò che hai fatto (a causa dell'utilizzo di funzionalità non documentate che sono cambiate tra 2 e 3). Documenta bene il tuo codice.
Ben Taylor,

84

La risposta di Jake è buona, ma se non vuoi usare un pool di thread (non sai di quanti thread avrai bisogno, ma creali secondo necessità), un buon modo per trasmettere informazioni tra thread è il built-in Classe Queue.Queue , in quanto offre sicurezza thread.

Ho creato il seguente decoratore per farlo agire in modo simile al threadpool:

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

Quindi lo usi come:

@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

La funzione decorata crea un nuovo thread ogni volta che viene chiamato e restituisce un oggetto Thread che contiene la coda che riceverà il risultato.

AGGIORNARE

È passato un po 'di tempo da quando ho pubblicato questa risposta, ma ottiene ancora visualizzazioni, quindi ho pensato di aggiornarlo per riflettere il modo in cui lo faccio nelle nuove versioni di Python:

Python 3.2 aggiunto nel concurrent.futuresmodulo che fornisce un'interfaccia di alto livello per attività parallele. Fornisce ThreadPoolExecutoreProcessPoolExecutor , quindi, è possibile utilizzare un thread o un pool di processi con la stessa API.

Un vantaggio di questa API è che l'invio di un'attività a un Executorreso aFuture oggetto oggetto, che si completerà con il valore di ritorno del callable .

Ciò rende queuesuperfluo il collegamento di un oggetto, il che semplifica parecchio il decoratore:

_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

Questo utilizzerà un modulo predefinito esecutore threadpool se non ne viene passato uno.

L'utilizzo è molto simile a prima:

@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

Se stai usando Python 3.4+, una caratteristica davvero interessante dell'uso di questo metodo (e degli oggetti Future in generale) è che il futuro restituito può essere spostato per trasformarlo in un asyncio.Futurewith asyncio.wrap_future. Questo lo fa funzionare facilmente con le coroutine:

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

Se non hai bisogno di accedere concurrent.Futureall'oggetto sottostante , puoi includere l'involucro nel decoratore:

_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

Quindi, ogni volta che è necessario rimuovere il codice cpu intensivo o bloccare il thread del loop degli eventi, è possibile inserirlo in una funzione decorata:

@threadpool
def some_long_calculation():
    ...

# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()

Non riesco a farlo funzionare; Ricevo un errore affermando che AttributeError: 'module' object has no attribute 'Lock'questo sembra emanare dalla linea y = long_task(10)... pensieri?
sadmic Microwave,

1
Il codice non utilizza esplicitamente Lock, quindi il problema potrebbe essere altrove nel codice. Potresti voler pubblicare una nuova domanda SO a riguardo
bj0

Perché result_queue è un attributo di istanza? Sarebbe meglio se fosse un attributo di classe in modo che gli utenti non debbano sapere di chiamare result_queue quando usano @threaded che non è esplicito e ambiguo?
nonbot

@ t88, non sai cosa intendi, hai bisogno di un modo per accedere al risultato, il che significa che devi sapere come chiamare. Se vuoi che sia qualcos'altro puoi sottoclassare Thread e fare quello che vuoi (questa era una soluzione semplice). Il motivo per cui la coda deve essere collegata al thread è che più chiamate / funzioni hanno le proprie code
bj0

1
È brillante! Grazie mille.
Ganesh Kathiresan,

53

Un'altra soluzione che non richiede la modifica del codice esistente:

import Queue
from threading import Thread

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

que = Queue.Queue()

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

Può anche essere facilmente adattato a un ambiente multi-thread:

import Queue
from threading import Thread

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

que = Queue.Queue()
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

t = Thread (target = lambda q, arg1: q.put (foo (arg1)), args = (que, 'world!')) che cosa fa q.put qui, che cosa fa Queue.Queue ()
vijay Shank

6
Dovrebbe esserci una tua statua nella tua città, grazie!
Onilol,

3
@Onilol - Grazie mille. Il tuo commento è esattamente il motivo per cui lo faccio :)
Arik

4
Per Python3, è necessario cambiare in from queue import Queue.
Gino Mempin,

1
Questo sembra essere il metodo meno dirompente (non è necessario ristrutturare in modo drammatico la base di codice originale) per consentire al valore di ritorno di tornare al thread principale.
Fanchen Bao,

24

La risposta join / returnrisposta di Parris / kindall trasferita su Python 3:

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

Nota, la Threadclasse è implementata diversamente in Python 3.


1
join accetta un parametro di timeout che deve essere passato
cz

22

Ho rubato la risposta di kindall e l'ho ripulita un po '.

La parte chiave è l'aggiunta di * args e ** kwargs a join () per gestire il timeout

class threadWithReturn(Thread):
    def __init__(self, *args, **kwargs):
        super(threadWithReturn, self).__init__(*args, **kwargs)

        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, *args, **kwargs):
        super(threadWithReturn, self).join(*args, **kwargs)

        return self._return

RISPOSTA AGGIORNATA QUI SOTTO

Questa è la mia risposta più popolare, quindi ho deciso di aggiornare con il codice che verrà eseguito su py2 e py3.

Inoltre, vedo molte risposte a questa domanda che mostrano una mancanza di comprensione riguardo a Thread.join (). Alcuni non riescono completamente a gestire l' timeoutarg. Ma c'è anche un caso angolare di cui dovresti essere consapevole riguardo alle istanze quando hai (1) una funzione target che può tornare Nonee (2) passi anche l' timeoutarg a join (). Vedere "TEST 4" per comprendere questo caso d'angolo.

Classe ThreadWithReturn che funziona con py2 e py3:

import sys
from threading import Thread
from builtins import super    # https://stackoverflow.com/a/30159479

if sys.version_info >= (3, 0):
    _thread_target_key = '_target'
    _thread_args_key = '_args'
    _thread_kwargs_key = '_kwargs'
else:
    _thread_target_key = '_Thread__target'
    _thread_args_key = '_Thread__args'
    _thread_kwargs_key = '_Thread__kwargs'

class ThreadWithReturn(Thread):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._return = None

    def run(self):
        target = getattr(self, _thread_target_key)
        if not target is None:
            self._return = target(
                *getattr(self, _thread_args_key),
                **getattr(self, _thread_kwargs_key)
            )

    def join(self, *args, **kwargs):
        super().join(*args, **kwargs)
        return self._return

Di seguito sono riportati alcuni test di esempio:

import time, random

# TEST TARGET FUNCTION
def giveMe(arg, seconds=None):
    if not seconds is None:
        time.sleep(seconds)
    return arg

# TEST 1
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',))
my_thread.start()
returned = my_thread.join()
# (returned == 'stringy')

# TEST 2
my_thread = ThreadWithReturn(target=giveMe, args=(None,))
my_thread.start()
returned = my_thread.join()
# (returned is None)

# TEST 3
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=2)
# (returned is None) # because join() timed out before giveMe() finished

# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))

Riesci a identificare il caso angolare che potremmo incontrare con TEST 4?

Il problema è che ci aspettiamo che giveMe () restituisca None (vedi TEST 2), ma prevediamo anche che join () restituisca None in caso di timeout.

returned is None significa:

(1) ecco cosa restituito da GiveMe (), oppure

(2) join () scaduto

Questo esempio è banale poiché sappiamo che giveMe () restituirà sempre None. Ma nell'istanza del mondo reale (in cui il bersaglio potrebbe legittimamente restituire Nessuno o qualcos'altro) vorremmo verificare esplicitamente l'accaduto.

Di seguito è come affrontare questo caso d'angolo:

# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))

if my_thread.isAlive():
    # returned is None because join() timed out
    # this also means that giveMe() is still running in the background
    pass
    # handle this based on your app's logic
else:
    # join() is finished, and so is giveMe()
    # BUT we could also be in a race condition, so we need to update returned, just in case
    returned = my_thread.join()

Conosci l'equivalente _Thread_target per Python3? Questo attributo non esiste in Python3.
GreySage,

Ho guardato nel file threading.py, si scopre che è _target (altri attributi hanno un nome simile).
GreySage,

Si potrebbe evitare di accedere alle variabili private della classe Thread, se si salva il target, argse kwargsgli argomenti a init come variabili membro della tua classe.
Tolli,

@GreySage Vedi la mia risposta, ho portato questo blocco su python3 qui sotto
GuySoft

La risposta di @GreySage ora supporta py2 e py3
user2426679

15

Utilizzando la coda:

import threading, queue

def calc_square(num, out_queue1):
  l = []
  for x in num:
    l.append(x*x)
  out_queue1.put(l)


arr = [1,2,3,4,5,6,7,8,9,10]
out_queue1=queue.Queue()
t1=threading.Thread(target=calc_square, args=(arr,out_queue1))
t1.start()
t1.join()
print (out_queue1.get())

1
Mi piace molto questo sollution, breve e dolce. Se la funzione legge una coda di input, e si aggiunge alla out_queue1dovrai ciclo su out_queue1.get()e prendere l'eccezione Queue.Empty: ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass. Punto e virgola per simulare le interruzioni di riga.
sastorsl,

6

La mia soluzione al problema è racchiudere la funzione e il thread in una classe. Non richiede l'utilizzo di pool, code o passaggio di variabili di tipo c. È anche non bloccante. Si controlla invece lo stato. Vedi l'esempio di come usarlo alla fine del codice.

import threading

class ThreadWorker():
    '''
    The basic idea is given a function create an object.
    The object can then run the function in a thread.
    It provides a wrapper to start it,check its status,and get data out the function.
    '''
    def __init__(self,func):
        self.thread = None
        self.data = None
        self.func = self.save_data(func)

    def save_data(self,func):
        '''modify function to save its returned data'''
        def new_func(*args, **kwargs):
            self.data=func(*args, **kwargs)

        return new_func

    def start(self,params):
        self.data = None
        if self.thread is not None:
            if self.thread.isAlive():
                return 'running' #could raise exception here

        #unless thread exists and is alive start or restart it
        self.thread = threading.Thread(target=self.func,args=params)
        self.thread.start()
        return 'started'

    def status(self):
        if self.thread is None:
            return 'not_started'
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return 'finished'

    def get_results(self):
        if self.thread is None:
            return 'not_started' #could return exception
        else:
            if self.thread.isAlive():
                return 'running'
            else:
                return self.data

def add(x,y):
    return x +y

add_worker = ThreadWorker(add)
print add_worker.start((1,2,))
print add_worker.status()
print add_worker.get_results()

come gestiresti un'eccezione? diciamo che è stata data la funzione add e int e una str. tutti i thread fallirebbero o fallirebbe solo uno?
user1745713

4

joinritorno sempre None, penso che dovresti sottoclassare Threadper gestire i codici di ritorno e così via.


4

Prendendo in considerazione il commento di @iman sulla risposta di @JakeBiesinger, l' ho ricomposto per avere un numero diverso di thread:

from multiprocessing.pool import ThreadPool

def foo(bar, baz):
    print 'hello {0}'.format(bar)
    return 'foo' + baz

numOfThreads = 3 
results = []

pool = ThreadPool(numOfThreads)

for i in range(0, numOfThreads):
    results.append(pool.apply_async(foo, ('world', 'foo'))) # tuple of args for foo)

# do some other stuff in the main process
# ...
# ...

results = [r.get() for r in results]
print results

pool.close()
pool.join()

Saluti,

Tipo.


2

È possibile definire un parametro modificabile sopra l'ambito della funzione threaded e aggiungere il risultato a quello. (Ho anche modificato il codice per essere compatibile con python3)

returns = {}
def foo(bar):
    print('hello {0}'.format(bar))
    returns[bar] = 'foo'

from threading import Thread
t = Thread(target=foo, args=('world!',))
t.start()
t.join()
print(returns)

Questo ritorna {'world!': 'foo'}

Se si utilizza l'input della funzione come chiave per il dict dei risultati, si garantisce che ogni input univoco inserisca i risultati


2

Sto usando questo wrapper, che trasforma comodamente qualsiasi funzione per l'esecuzione in un Thread- prendendosi cura del suo valore di ritorno o eccezione. Non aggiunge Queuesovraccarico.

def threading_func(f):
    """Decorator for running a function in a thread and handling its return
    value or exception"""
    def start(*args, **kw):
        def run():
            try:
                th.ret = f(*args, **kw)
            except:
                th.exc = sys.exc_info()
        def get(timeout=None):
            th.join(timeout)
            if th.exc:
                raise th.exc[0], th.exc[1], th.exc[2] # py2
                ##raise th.exc[1] #py3                
            return th.ret
        th = threading.Thread(None, run)
        th.exc = None
        th.get = get
        th.start()
        return th
    return start

Esempi di utilizzo

def f(x):
    return 2.5 * x
th = threading_func(f)(4)
print("still running?:", th.is_alive())
print("result:", th.get(timeout=1.0))

@threading_func
def th_mul(a, b):
    return a * b
th = th_mul("text", 2.5)

try:
    print(th.get())
except TypeError:
    print("exception thrown ok.")

Note sul threadingmodulo

Il comodo valore di ritorno e la gestione delle eccezioni di una funzione filettata sono un bisogno frequente di "Pythonic" e dovrebbero essere già offerti dal threadingmodulo, possibilmente direttamente nella Threadclasse standard . ThreadPoolha troppe spese generali per compiti semplici: 3 discussioni di gestione, molta burocrazia. Sfortunatamente Threadil layout è stato copiato originariamente da Java, come si può vedere dal primo parametro del costruttore (!) Ancora inutile group.


il primo costruttore non è inutile, è riservato lì per future implementazioni .. dal ricettario di programmazione parallela di Python
vijay shanker,

1

Definisci il tuo obiettivo su
1) accetta un argomento q
2) sostituisci qualsiasi istruzione return fooconq.put(foo); return

quindi una funzione

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

potrebbe diventare

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

e poi procederesti come tale

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

E puoi usare decoratori / wrapper di funzioni per farlo in modo da poter usare le tue funzioni esistenti come targetsenza modificarle, ma segui questo schema di base.


Dovrebbe essereresults = [ans_q.get() for _ in xrange(len(threads))]
Hemant H Kumar

1

Come accennato, il pool di elaborazione multipla è molto più lento del threading di base. L'uso delle code come proposto in alcune risposte qui è un'alternativa molto efficace. L'ho usato con i dizionari per essere in grado di eseguire molti piccoli thread e recuperare più risposte combinandole con i dizionari:

#!/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)

1

L'idea di GuySoft è fantastica, ma penso che l'oggetto non debba necessariamente ereditare da Thread e start () potrebbe essere rimosso dall'interfaccia:

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

0

Una solita soluzione è quella di avvolgere la tua funzione foocon un decoratore come

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

Quindi l'intero codice potrebbe apparire così

result = queue.Queue()

def task_wrapper(*args):
    result.put(target(*args))

threads = [threading.Thread(target=task_wrapper, args=args) for args in args_list]

for t in threads:
    t.start()
    while(True):
        if(len(threading.enumerate()) < max_num):
            break
for t in threads:
    t.join()
return result

Nota

Un problema importante è che i valori restituiti potrebbero non essere compromessi . (In effetti, return valuenon viene necessariamente salvato in queue, poiché è possibile scegliere una struttura di dati arbitraria e sicura per i thread )


0

Perché non usare solo la variabile globale?

import threading


class myThread(threading.Thread):
    def __init__(self, ind, lock):
        threading.Thread.__init__(self)
        self.ind = ind
        self.lock = lock

    def run(self):
        global results
        with self.lock:
            results.append(self.ind)



results = []
lock = threading.Lock()
threads = [myThread(x, lock) for x in range(1, 4)]
for t in threads:
    t.start()
for t in threads:
    t.join()
print(results)

0

La risposta di Kindall in Python3

class ThreadWithReturnValue(Thread):
    def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs={}, *, daemon=None):
        Thread.__init__(self, group, target, name, args, kwargs, daemon)
        self._return = None 

    def run(self):
        try:
            if self._target:
                self._return = self._target(*self._args, **self._kwargs)
        finally:
            del self._target, self._args, self._kwargs 

    def join(self,timeout=None):
        Thread.join(self,timeout)
        return self._return

-2

Se solo Vero o Falso deve essere convalidato dalla chiamata di una funzione, una soluzione più semplice che trovo sta aggiornando un elenco globale.

import threading

lists = {"A":"True", "B":"True"}

def myfunc(name: str, mylist):
    for i in mylist:
        if i == 31:
            lists[name] = "False"
            return False
        else:
            print("name {} : {}".format(name, i))

t1 = threading.Thread(target=myfunc, args=("A", [1, 2, 3, 4, 5, 6], ))
t2 = threading.Thread(target=myfunc, args=("B", [11, 21, 31, 41, 51, 61], ))
t1.start()
t2.start()
t1.join()
t2.join()

for value in lists.values():
    if value == False:
        # Something is suspicious 
        # Take necessary action 

Questo è più utile dove vuoi trovare se uno dei thread ha restituito un falso stato per eseguire l'azione necessaria.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.