Comment obtenir la valeur 'foo'
qui est renvoyée par la cible du 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()
La "manière évidente de le faire", illustrée ci-dessus, ne fonctionne pas: thread.join()
a retourné None
.
FWIW, le module multiprocessing
a une interface Nice pour cela en utilisant la classe Pool
. Et si vous souhaitez vous en tenir à des threads plutôt qu'à des processus, vous pouvez simplement utiliser la classe multiprocessing.pool.ThreadPool
comme solution de remplacement.
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.
Une façon que j'ai vue consiste à passer un objet mutable, tel qu'une liste ou un dictionnaire, au constructeur du thread, avec un index ou un autre identifiant. Le thread peut ensuite stocker ses résultats dans son emplacement dédié dans cet objet. Par exemple:
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?
Si vous voulez vraiment que join()
renvoie la valeur de retour de la fonction appelée, vous pouvez le faire avec une sous-classe Thread
comme celle-ci:
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
Cela devient un peu poilu à cause de certains noms malchanceux, et il accède à des structures de données "privées" qui sont spécifiques à la mise en œuvre de Thread
... mais ça fonctionne.
Pour 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
La réponse de Jake est bonne, mais si vous ne voulez pas utiliser de pool de threads (vous ne savez pas combien de threads vous aurez besoin, mais créez-les au besoin), alors un bon moyen de transmettre des informations entre les threads est la fonction intégrée. Queue.Queue class, car il offre la sécurité des threads.
J'ai créé le décorateur suivant pour le faire agir de la même manière que le 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
Ensuite, vous l'utilisez comme:
@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 fonction décorée crée un nouveau thread à chaque appel et renvoie un objet Thread contenant la file d'attente qui recevra le résultat.
METTRE À JOUR
Cela fait un bon bout de temps que je n'ai pas posté cette réponse, mais elle a toujours des vues, alors j'ai pensé la mettre à jour pour refléter ma façon de procéder dans les nouvelles versions de Python:
Python 3.2 ajouté dans le module concurrent.futures
qui fournit une interface de haut niveau pour les tâches parallèles. Il fournit ThreadPoolExecutor
et ProcessPoolExecutor
, de sorte que vous pouvez utiliser un pool de processus ou de processus avec le même api.
L'un des avantages de cette API est que la soumission d'une tâche à une Executor
renvoie un objet Future
, qui se termine par la valeur de retour de l'appelable que vous soumettez.
Cela rend inutile d'attacher un objet queue
, ce qui simplifie un peu le décorateur:
_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
Ceci utilisera un exécuteur module} _ par défaut, threadpool, par défaut.
L'utilisation est très similaire à avant:
@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
Si vous utilisez Python 3.4+, l’utilisation de cette méthode (et des objets Future en général) permet de transformer le futur retour en un asyncio.Future
avec asyncio.wrap_future
. Cela facilite le travail avec les coroutines:
result = await asyncio.wrap_future(long_task(10))
Si vous n'avez pas besoin d'accéder à l'objet concurrent.Future
sous-jacent, vous pouvez inclure le wrap dans le décorateur:
_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
Ensuite, chaque fois que vous avez besoin de pousser un processeur intensif ou de bloquer le code hors du fil de la boucle d’événements, vous pouvez le placer dans une fonction décorée:
@threadpool
def some_long_calculation():
...
# this will suspend while the function is executed on a threadpool
result = await some_long_calculation()
Une autre solution qui ne nécessite pas de changer votre code existant:
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
Il peut également être facilement ajusté à un environnement multithread:
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
Parris/kindall's answerjoin
/return
réponse transférée vers 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
Notez que la classe Thread
est implémentée différemment dans Python 3.
J'ai volé la réponse de kindall et l'ai nettoyée juste un petit peu.
La partie clé est d'ajouter * args et ** kwargs à join () afin de gérer le délai d'attente
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
RÉPONSE MISE À JOUR CI-DESSOUS
C’est ma réponse la plus populaire et la plus votée, j’ai donc décidé de mettre à jour le code qui s’exécutera à la fois sur py2 et py3.
De plus, je vois beaucoup de réponses à cette question qui montrent un manque de compréhension concernant Thread.join (). Certains échouent complètement à gérer la timeout
arg. Mais il y a aussi un cas d'école dont vous devez être conscient en ce qui concerne les cas où vous avez (1) une fonction cible pouvant renvoyer None
et (2) vous transmettez également l'argument timeout
arg à join (). Veuillez voir "TEST 4" pour comprendre ce cas de coin.
Classe ThreadWithReturn qui fonctionne avec py2 et 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
Quelques exemples de tests sont présentés ci-dessous:
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))
Pouvez-vous identifier le coin cas que nous pourrions éventuellement rencontrer avec TEST 4?
Le problème est que nous nous attendons à ce que donMe () renvoie Aucun (voir TEST 2), mais nous nous attendons également à ce que join () renvoie Aucun si le délai est dépassé.
returned is None
signifie soit:
(1) c'est ce que GiveMe () a retourné, ou
(2) join () expiré
Cet exemple est trivial puisque nous savons que giveMe () retournera toujours None. Mais dans le cas concret (où la cible peut légitimement renvoyer Aucun ou autre chose), nous voudrions vérifier explicitement ce qui s'est passé.
Vous trouverez ci-dessous comment aborder ce coin de cas:
# 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()
Utiliser la file d'attente:
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())
Ma solution au problème consiste à envelopper la fonction et le fil dans une classe. Ne nécessite pas l'utilisation de pools, de files d'attente ou de variables de type c. C'est aussi non bloquant. Vous vérifiez le statut à la place. Voir un exemple d'utilisation en fin de code.
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()
Vous pouvez définir un mutable au-dessus de la portée de la fonction thread et y ajouter le résultat. (J'ai aussi modifié le code pour qu'il soit compatible 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)
Ceci retourne {'world!': 'foo'}
Si vous utilisez la fonction entrée comme clé de votre dict de résultats, chaque entrée unique est garantie pour donner une entrée dans les résultats
Compte tenu de@imancommenter@JakeBiesingeranswer je l'ai recomposé pour avoir un nombre de threads différent:
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()
À votre santé,
Gars.
Vous pouvez utiliser Pool en tant que pool de processus de travail, comme ci-dessous:
from multiprocessing import Pool
def f1(x, y):
return x*y
if __== '__main__':
with Pool(processes=10) as pool:
result = pool.apply(f1, (2, 3))
print(result)
J'utilise cette enveloppe qui convertit aisément n'importe quelle fonction pour exécuter une Thread
- en prenant en compte sa valeur de retour ou son exception. Cela n'ajoute pas Queue
frais généraux.
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
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.")
threading
Une valeur de retour confortable et la gestion des exceptions d'une fonction thread sont un besoin "pythonique" fréquent et devraient en fait déjà être offerts par le module threading
- éventuellement directement dans la classe standard Thread
. ThreadPool
a trop de frais généraux pour des tâches simples - 3 tâches de gestion, beaucoup de bureaucratie. Malheureusement, la disposition de Thread
a été copiée à partir de Java - vous le voyez par exemple. du 1 er (!) constructeur toujours utilisable, paramètre group
.
Comme mentionné, le pool de multitraitement est beaucoup plus lent que le threading de base. L'utilisation de files d'attente comme proposé dans certaines réponses constitue une alternative très efficace. Je l'ai utilisé avec des dictionnaires afin de pouvoir exécuter beaucoup de petits threads et récupérer plusieurs réponses en les combinant avec des dictionnaires:
#!/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)
join
retournera toujours None
, je pense que vous devriez sous-classe Thread
pour gérer les codes de retour, etc.
Une solution habituelle consiste à envelopper votre fonction foo
avec un décorateur comme
result = queue.Queue()
def task_wrapper(*args):
result.put(target(*args))
Alors tout le code peut ressembler à ça
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
Un problème important est que les valeurs de retour peuvent être unorderred . (En fait, le return value
n'est pas nécessairement enregistré dans la queue
, car vous pouvez choisir arbitraire thread-safe data structure)
Définissez votre cible pour
1) prendre un argument q
2) remplacer toutes les instructions return foo
par q.put(foo); return
donc une fonction
def func(a):
ans = a * a
return ans
deviendrait
def func(a, q):
ans = a * a
q.put(ans)
return
et alors vous procéderiez comme tel
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))]
Et vous pouvez utiliser les décorateurs/wrappers de fonctions pour le rendre ainsi vous pouvez utiliser vos fonctions existantes en tant que target
sans les modifier, mais suivez ce schéma de base.
Pourquoi ne pas simplement utiliser la variable 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)
Si seul True ou False doit être validé à partir de l'appel d'une fonction, une solution plus simple que je trouve est la mise à jour d'une liste 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
Cela est plus utile lorsque vous voulez savoir si l’un des threads a renvoyé un faux statut pour prendre les mesures nécessaires.
L'idée de GuySoft est excellente, mais je pense que l'objet ne doit pas nécessairement hériter de Thread et que start () pourrait être supprimé de l'interface:
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
La réponse de Kindall en 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
Un moyen très simple de faire cela pour des mannequins comme moi:
import queue
import threading
# creating queue instance
q = queue.Queue()
# creating threading class
class AnyThread():
def __init__ (self):
threading.Thread.__init__(self)
def run(self):
# in this class and function we will put our test target function
test()
t = AnyThread()
# having our test target function
def test():
# do something in this function:
result = 3 + 2
# and put result to a queue instance
q.put(result)
for i in range(3): #calling our threading fucntion 3 times (just for example)
t.run()
output = q.get() # here we get output from queue instance
print(output)
>>> 5
>>> 5
>>> 5
la chose principale ici - est le module queue
. Nous créons une instance queue.Queue()
et l'incluons dans notre fonction. Nous le nourrissons avec notre résultat que nous dépassons plus tard.
Veuillez voir un autre exemple avec les arguments passés à notre fonction de test:
import queue
import threading
# creating queue instance
q = queue.Queue()
# creating threading class
class AnyThread():
def __init__ (self):
threading.Thread.__init__(self)
def run(self, a, b):
# in this class and function we will put our execution test function
test(a, b)
t = AnyThread()
# having our test target function
def test(a, b):
# do something in this function:
result = a + b
# and put result to a queue instance
q.put(result)
for i in range(3): #calling our threading fucntion 3 times (just for example)
t.run(3+i, 2+i)
output = q.get() # here we get output from queue instance
print(output)
>>> 5
>>> 7
>>> 9