66 votes

Valeur de retour du fil

Comment faire en sorte qu'un thread renvoie un tuple ou toute autre valeur de mon choix au parent en Python ?

68voto

Alex Martelli Points 330805

Je vous suggère d'instancier un Queue.Queue avant de démarrer le thread, et le passe comme l'un des arguments du thread : avant que le thread ne se termine, il .put s le résultat sur la file d'attente qu'il a reçue en argument. Le parent peut .get o .get_nowait à volonté.

Les files d'attente sont généralement la meilleure façon d'organiser la synchronisation des threads et la communication en Python : elles sont intrinsèquement sûres pour les threads, les véhicules de passage de messages -- la meilleure façon d'organiser le multitâche en général!-)

2 votes

before the thread finishes, it .puts the result on the queue it received as an argument vous voulez dire que ce sera fait automatiquement par python ? si non (c'est un conseil de conception) alors pourriez-vous le préciser dans la réponse.

3 votes

Il est laid de spécialiser une fonction existante pour cela ; et la file d'attente a beaucoup de surcharge inutile pour un résultat unique. Une sous-classe plus claire et plus efficace threading.Thread et la nouvelle méthode run() stocke simplement le résultat comme attribut comme self.ret = ... (Il serait beaucoup plus confortable d'avoir une sous-classe de Thread qui gère les valeurs de retour / exceptions de la fonction cible personnalisée. En effet, threading.Thread devrait être étendu pour offrir cela d'emblée - car cela serait compatible avec l'ancien comportement "return None").

1 votes

L'utilisation de Queue est la meilleure réponse, cependant, le post ci-dessus illustre mal comment pour utiliser la file d'attente. Voir este qui fournit un exemple de base avec une file d'attente et des valeurs de retour.

15voto

Fatih Karatana Points 481

Vous devez passer une instance de file d'attente en tant que paramètre puis vous devez .put() votre objet de retour dans la file d'attente. Vous pouvez récupérer la valeur de retour via queue.get() quel que soit l'objet que vous avez mis.

Un échantillon :

queue = Queue.Queue()
thread_ = threading.Thread(
                target=target_method,
                name="Thread1",
                args=[params, queue],
                )
thread_.start()
thread_.join()
queue.get()

def target_method(self, params, queue):
 """
 Some operations right here
 """
 your_return = "Whatever your object is"
 queue.put(your_return)

À utiliser pour plusieurs fils :

#Start all threads in thread pool
    for thread in pool:
        thread.start()
        response = queue.get()
        thread_results.append(response)

#Kill all threads
    for thread in pool:
        thread.join()

J'utilise cette mise en œuvre et elle fonctionne très bien pour moi. Je vous souhaite de faire de même.

1 votes

Bien sûr que je commence le fil de discussion, j'ai juste oublié de mettre la ligne ici :) Merci pour la remarque.

0 votes

Comment cela se présenterait-il si vous aviez plusieurs threads ? que.get() renvoie le résultat d'un seul thread pour moi ?

1 votes

Dans le cas de threads multiples, il semble que l'option response = queue.get() augmenterait le Empty exception si le thread n'était pas encore terminé et probablement se terminer avec une exception gérée. Même si cela réussissait à chaque fois, cela signifierait que chaque thread est terminé et que peu ou pas de multithreading réel n'a eu lieu.

12voto

Peter Hansen Points 8487

Si vous appelez join() pour attendre que le fil se termine, vous pouvez simplement attacher le résultat à l'instance Thread elle-même et le récupérer dans le fil principal après le retour de join().

D'autre part, vous ne nous dites pas comment vous comptez découvrir que le fil est fait et que le résultat est disponible. Si vous avez déjà un moyen de le faire, il vous indiquera probablement (et à nous, si vous nous le disiez) la meilleure façon de diffuser les résultats.

0 votes

vous pourriez simplement attacher le résultat à l'instance Thread elle-même Comment passer l'instance Thread à la cible qu'elle exécute pour que la cible puisse attacher le résultat à cette instance ?

1 votes

Piotr Dobrogost, si vous ne sous-classez pas Thread pour votre instance, vous pouvez simplement utiliser threading.current_thread() à la fin de votre callable cible. Je dirais que c'est un peu laid, mais l'approche d'Alex a toujours été la plus élégante. Celle-ci est simplement plus pratique dans certains cas.

8 votes

Ce serait bien si join() renverrait simplement ce que la méthode appelée renvoie... il semble idiot qu'au lieu de cela il renvoie None .

5voto

Vijay Mathew Points 17155

Une autre approche consiste à passer une fonction de rappel au thread. Cela offre un moyen simple, sûr et flexible de renvoyer une valeur au parent, à tout moment, à partir du nouveau thread.

# A sample implementation

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, cb):
        threading.Thread.__init__(self)
        self.callback = cb

    def run(self):
        for i in range(10):
            self.callback(i)
            time.sleep(1)

# test

import sys

def count(x):
    print x
    sys.stdout.flush()

t = MyThread(count)
t.start()

9 votes

Le problème est que le rappel s'exécute toujours dans le fil d'exécution enfant, plutôt que dans le fil d'origine.

0 votes

@wilberforce pouvez-vous expliquer quels problèmes cela peut causer ?

4 votes

Ok. Par exemple, si le callback écrit dans un fichier journal dans lequel le thread parent écrit également pendant que le thread est en cours d'exécution. Puisque le callback est exécuté dans le thread enfant, il y a un risque que les deux écritures se produisent en même temps et entrent en collision - vous pourriez obtenir une sortie déformée ou entrelacée, ou un plantage si le cadre de journalisation fait de la comptabilité interne. L'utilisation d'une file d'attente thread-safe et l'exécution de toutes les écritures par un seul thread permettraient d'éviter ce problème. Ces types de problèmes peuvent être désagréables car ils ne sont pas déterministes - ils peuvent n'apparaître qu'en production et être difficiles à reproduire.

1voto

beardedprojamz Points 801

Eh bien, dans le module Python threading, il existe des objets de condition qui sont associés aux verrous. Une méthode acquire() renverra la valeur renvoyée par la méthode sous-jacente. Pour plus d'informations : Objets de condition Python

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X