La fonction foo
ci-dessous renvoie une chaîne 'foo'
. Comment puis-je obtenir la valeur 'foo'
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 "façon évidente de le faire", illustrée ci-dessus, ne fonctionne pas: thread.join()
retournée None
.
futures = [executor.submit(foo, param) for param in param_list]
La commande sera maintenue et la sortie duwith
permettra la collecte des résultats.[f.result() for f in futures]
FWIW, le
multiprocessing
module a une belle interface pour cela en utilisant laPool
classe. Et si vous voulez vous en tenir aux threads plutôt qu'aux processus, vous pouvez simplement utiliser lamultiprocessing.pool.ThreadPool
classe comme remplacement de remplacement.la source
multiprocess
, ils n'ont rien à voir avec les processus.processes=1
plusieurs si vous avez plus de threads!Une façon que j'ai vu est de passer un objet mutable, comme une liste ou un dictionnaire, au constructeur du thread, avec un index ou un autre identifiant quelconque. Le thread peut ensuite stocker ses résultats dans son emplacement dédié dans cet objet. Par exemple:
Si vous voulez vraiment
join()
renvoyer la valeur de retour de la fonction appelée, vous pouvez le faire avec uneThread
sous - classe comme celle-ci:Cela devient un peu poilu à cause de certains changements de nom, et il accède à des structures de données "privées" qui sont spécifiques à la
Thread
mise en œuvre ... mais cela fonctionne.Pour python3
la source
threading
, pas une bibliothèque différente à essayer, plus la limitation de la taille du pool introduit un problème potentiel supplémentaire, qui s'est produit dans mon cas.TypeError: __init__() takes from 1 to 6 positional arguments but 7 were given
. Une façon de résoudre ce problème?_Thread__target
chose). Vous ferez en sorte que quiconque essaie de porter votre code sur python 3 vous déteste jusqu'à ce qu'il comprenne ce que vous avez fait (en raison de l'utilisation de fonctionnalités non documentées qui ont changé entre 2 et 3). Documentez bien votre code.La réponse de Jake est bonne, mais si vous ne voulez pas utiliser un 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 Classe Queue.Queue , car elle offre la sécurité des threads.
J'ai créé le décorateur suivant pour le faire agir de manière similaire au threadpool:
Ensuite, vous l'utilisez simplement comme:
La fonction décorée crée un nouveau thread à chaque appel et renvoie un objet Thread qui contient la file d'attente qui recevra le résultat.
METTRE À JOUR
Cela fait un bon moment que j'ai posté cette réponse, mais elle obtient toujours des vues, alors j'ai pensé que je la mettrais à jour pour refléter la façon dont je fais cela dans les nouvelles versions de Python:
Python 3.2 ajouté dans le
concurrent.futures
module qui fournit une interface de haut niveau pour les tâches parallèles. Il fournitThreadPoolExecutor
etProcessPoolExecutor
vous pouvez donc utiliser un thread ou un pool de processus avec la même API.Un avantage de cette API est que la soumission d'une tâche à un
Executor
retourne unFuture
objet, qui se terminera avec la valeur de retour de l'appelable que vous soumettez.Cela rend
queue
inutile d' attacher un objet, ce qui simplifie un peu le décorateur:Cela utilisera un exécuteur de pool de threads de module par défaut s'il n'est pas transmis.
L'utilisation est très similaire à celle d'avant:
Si vous utilisez Python 3.4+, une fonctionnalité vraiment intéressante de l'utilisation de cette méthode (et des objets Future en général) est que le futur retourné peut être encapsulé pour le transformer en un
asyncio.Future
avecasyncio.wrap_future
. Cela le fait fonctionner facilement avec les coroutines:Si vous n'avez pas besoin d'accéder à l'
concurrent.Future
objet sous-jacent , vous pouvez inclure l'habillage dans le décorateur:Ensuite, chaque fois que vous devez pousser le code intensif ou bloquant du processeur hors du thread de la boucle d'événements, vous pouvez le mettre dans une fonction décorée:
la source
AttributeError: 'module' object has no attribute 'Lock'
cela semble émaner de la ligney = long_task(10)
... des pensées?Une autre solution qui ne nécessite pas de changer votre code existant:
Il peut également être facilement adapté à un environnement multi-thread:
la source
from queue import Queue
.Parris / kindall's answer
join
/return
answer ported to Python 3:Remarque, la
Thread
classe est implémentée différemment dans Python 3.la source
J'ai volé la réponse de Kindall et l'ai nettoyée un peu.
La partie clé est d'ajouter * args et ** kwargs à join () afin de gérer le délai d'attente
RÉPONSE MISE À JOUR CI-DESSOUS
C'est ma réponse la plus votée, j'ai donc décidé de mettre à jour avec du code qui fonctionnera à la fois sur py2 et py3.
De plus, je vois de nombreuses réponses à cette question qui montrent un manque de compréhension concernant Thread.join (). Certains échouent complètement à gérer l'
timeout
argument. Mais il y a aussi un cas d'angle que vous devez connaître en ce qui concerne les cas où vous avez (1) une fonction cible qui peut retournerNone
et (2) vous passez également l'timeout
argument à join (). Veuillez voir "TEST 4" pour comprendre ce boîtier d'angle.Classe ThreadWithReturn qui fonctionne avec py2 et py3:
Quelques exemples de tests sont présentés ci-dessous:
Pouvez-vous identifier le cas d'angle que nous pourrions éventuellement rencontrer avec TEST 4?
Le problème est que nous nous attendons à ce que giveMe () retourne None (voir TEST 2), mais nous nous attendons également à ce que join () retourne None s'il expire.
returned is None
signifie soit:(1) c'est ce que giveMe () a renvoyé, ou
(2) rejoindre () expiré
Cet exemple est trivial car nous savons que giveMe () retournera toujours None. Mais dans le cas réel (où la cible peut légitimement renvoyer None ou autre chose), nous voudrions vérifier explicitement ce qui s'est passé.
Voici comment résoudre ce cas d'angle:
la source
target
,args
et leskwargs
arguments pour initialiser les variables membres de votre classe.Utilisation de la file d'attente:
la source
out_queue1
vous devez boucle surout_queue1.get()
et attraper l'exception Queue.Empty:ret = [] ; try: ; while True; ret.append(out_queue1.get(block=False)) ; except Queue.Empty: ; pass
. Des points-virgules pour simuler les sauts de ligne.Ma solution au problème consiste à encapsuler la fonction et le thread dans une classe. Ne nécessite pas l'utilisation de pools, de files d'attente ou de transmission de variables de type c. Il est également non bloquant. Vous vérifiez plutôt le statut. Voir l'exemple de la façon de l'utiliser à la fin du code.
la source
join
toujours retournerNone
, je pense que vous devriez sous-classeThread
pour gérer les codes de retour et ainsi.la source
Compte tenu du commentaire de @iman sur la réponse de @JakeBiesinger, je l'ai recomposé pour avoir un certain nombre de threads:
À votre santé,
Gars.
la source
Vous pouvez définir un mutable au-dessus de la portée de la fonction filetée et ajouter le résultat à cela. (J'ai également modifié le code pour qu'il soit compatible python3)
Cela revient
{'world!': 'foo'}
Si vous utilisez l'entrée de fonction comme clé de votre dict de résultats, chaque entrée unique est garantie de donner une entrée dans les résultats
la source
J'utilise ce wrapper, qui transforme confortablement n'importe quelle fonction pour fonctionner dans un
Thread
- en prenant soin de sa valeur de retour ou de son exception. Cela n'ajoute pas deQueue
frais généraux.Exemples d'utilisation
Notes sur le
threading
moduleLa valeur de retour confortable et la gestion des exceptions d'une fonction filetée est un besoin "Pythonique" fréquent et devrait en effet déjà être proposé par le
threading
module - éventuellement directement dans laThread
classe standard .ThreadPool
a beaucoup trop de frais généraux pour les tâches simples - 3 threads de gestion, beaucoup de bureaucratie. Malheureusement,Thread
la mise en page a été copiée à partir de Java à l'origine - que vous voyez par exemple à partir du 1er paramètre constructeur (!) Encore inutilegroup
.la source
Définissez votre cible à
1) prenez un argument
q
2) remplacez toutes les déclarations
return foo
parq.put(foo); return
donc une fonction
deviendrait
puis vous procéderiez comme tel
Et vous pouvez utiliser des décorateurs / wrappers de fonction pour le faire afin que vous puissiez utiliser vos fonctions existantes
target
sans les modifier, mais suivez ce schéma de base.la source
results = [ans_q.get() for _ in xrange(len(threads))]
Comme mentionné, le pool de multitraitement est beaucoup plus lent que le thread de base. L'utilisation des files d'attente comme proposé dans certaines réponses est 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:
la source
L'idée de GuySoft est géniale, mais je pense que l'objet n'a pas nécessairement à hériter de Thread et start () pourrait être supprimé de l'interface:
la source
Une solution habituelle consiste à envelopper votre fonction
foo
avec un décorateur commeEnsuite, tout le code peut ressembler à ça
Remarque
Un problème important est que les valeurs de retour peuvent ne pas être ordonnées . (En fait, le
return value
n'est pas nécessairement sauvegardé dans lequeue
, car vous pouvez choisir une structure de données thread-safe arbitraire )la source
Pourquoi ne pas simplement utiliser une variable globale?
la source
La réponse de Kindall en Python3
la source
Si seul True ou False doit être validé à partir de l'appel d'une fonction, une solution plus simple que je trouve consiste à mettre à jour une liste globale.
Cela est plus utile lorsque vous souhaitez rechercher si l'un des threads a renvoyé un faux état pour prendre les mesures nécessaires.
la source