web-dev-qa-db-fra.com

Renvoie la valeur du thread

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

54
Paul Joseph

Je vous suggère d'instancier un Queue.Queue avant de démarrer le thread et de le transmettre comme argument du thread: avant que le thread ne se termine, il .puts le résultat de la file qu'il a reçu en argument. Le parent peut .get ou .get_nowait à volonté.

Les files d’attente sont généralement le meilleur moyen d’organiser la synchronisation des threads et la communication en Python: ce sont des véhicules de transmission de messages intrinsèquement sécurisés pour les threads - le meilleur moyen d’organiser le multitâche en général! -)

59
Alex Martelli

Si vous appeliez join () pour attendre la fin du thread, vous pouvez simplement attacher le résultat à l'occurrence Thread elle-même, puis le récupérer depuis le thread principal après le retour de join (). 

D'autre part, vous ne nous dites pas comment vous avez l'intention de découvrir que le fil est terminé et que le résultat est disponible. Si vous avez déjà un moyen de le faire, cela vous orientera probablement (et nous, si vous nous le dites) sur le meilleur moyen d'obtenir les résultats.

12
Peter Hansen

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 rassembler la valeur de retour via queue.get (), quel que soit l'objet que vous avez mis.

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

Utilisez pour plusieurs threads:

#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 implémentation et cela fonctionne très bien pour moi. Je vous souhaite de le faire.

12
Fatih Karatana

Utilisez lambda pour envelopper votre fonction de fil cible et renvoyer sa valeur de retour au fil parent à l’aide de queue . (Votre fonction cible d'origine reste inchangée sans paramètre de file d'attente supplémentaire.)

Exemple de code:

import threading
import queue
def dosomething(param):
    return param * 2
que = queue.Queue()
thr = threading.Thread(target = lambda q, arg : q.put(dosomething(arg)), args = (que, 2))
thr.start()
thr.join()
while not que.empty():
    print(que.get())

Sortie:

4
7
Petr Vepřek

Je suis surpris que personne ne dise que vous pourriez simplement passer un mutable:

>>> thread_return={'success': False}
>>> from threading import Thread
>>> def task(thread_return):
...  thread_return['success'] = True
... 
>>> Thread(target=task, args=(thread_return,)).start()
>>> thread_return
{'success': True}

peut-être que cela pose des problèmes majeurs dont je ne suis pas au courant.

7
jcomeau_ictx

Une autre approche consiste à transmettre 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()
5
Vijay Mathew

Vous pouvez utiliser synchronized queue module.
Pensez que vous devez vérifier les informations d’utilisateur d’une base de données avec un identifiant connu:

def check_infos(user_id, queue):
    result = send_data(user_id)
    queue.put(result)

Maintenant, vous pouvez obtenir vos données comme ceci:

import queue, threading
queued_request = queue.Queue()
check_infos_thread = threading.Thread(target=check_infos, args=(user_id, queued_request))
check_infos_thread.start()
final_result = queued_request.get()
3
BoCyrill

POC:

import random
import threading

class myThread( threading.Thread ):
    def __init__( self, arr ):
        threading.Thread.__init__( self )
        self.arr = arr
        self.ret = None

    def run( self ):
        self.myJob( self.arr )

    def join( self ):
        threading.Thread.join( self )
        return self.ret

    def myJob( self, arr ):
        self.ret = sorted( self.arr )
        return

#Call the main method if run from the command line.
if __== '__main__':
    N = 100

    arr = [ random.randint( 0, 100 ) for x in range( N ) ]
    th = myThread( arr )
    th.start( )
    sortedArr = th.join( )

    print "arr2: ", sortedArr
2
husanu

Basé sur la suggestion de jcomeau_ictx. Le plus simple que j'ai rencontré. La condition requise ici était d’obtenir l’état de sortie de trois processus différents exécutés sur le serveur et de déclencher un autre script si les trois aboutissaient. Cela semble bien fonctionner

  class myThread(threading.Thread):
        def __init__(self,threadID,pipePath,resDict):
            threading.Thread.__init__(self)
            self.threadID=threadID
            self.pipePath=pipePath
            self.resDict=resDict

        def run(self):
            print "Starting thread %s " % (self.threadID)
            if not os.path.exists(self.pipePath):
            os.mkfifo(self.pipePath)
            pipe_fd = os.open(self.pipePath, os.O_RDWR | os.O_NONBLOCK )
           with os.fdopen(pipe_fd) as pipe:
                while True:
                  try:
                     message =  pipe.read()
                     if message:
                        print "Received: '%s'" % message
                        self.resDict['success']=message
                        break
                     except:
                        pass

    tResSer={'success':'0'}
    tResWeb={'success':'0'}
    tResUisvc={'success':'0'}


    threads = []

    pipePathSer='/tmp/path1'
    pipePathWeb='/tmp/path2'
    pipePathUisvc='/tmp/path3'

    th1=myThread(1,pipePathSer,tResSer)
    th2=myThread(2,pipePathWeb,tResWeb)
    th3=myThread(3,pipePathUisvc,tResUisvc)

    th1.start()
    th2.start()
    th3.start()

    threads.append(th1)
    threads.append(th2)
    threads.append(th3)

    for t in threads:
        print t.join()

    print "Res: tResSer %s tResWeb %s tResUisvc %s" % (tResSer,tResWeb,tResUisvc)
    # The above statement prints updated values which can then be further processed
1
f-z-N

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

1
Ben Hayden

Pour les programmes faciles, les réponses ci-dessus me paraissent un peu excessives. Je voudrais en-niven l'approche mutable:

class RetVal:
 def __init__(self):
   self.result = None


def threadfunc(retVal):
  retVal.result = "your return value"

retVal = RetVal()
thread = Thread(target = threadfunc, args = (retVal))

thread.start()
thread.join()
print(retVal.result)
0
TheTrowser

La fonction wrapper suivante encapsulera une fonction existante et retournera un objet qui pointe à la fois vers le thread (pour que vous puissiez appeler start(), join(), etc.) ainsi que pour accéder/voir sa valeur de retour éventuelle.

def threadwrap(func,args,kwargs):
   class res(object): result=None
   def inner(*args,**kwargs): 
     res.result=func(*args,**kwargs)
   import threading
   t = threading.Thread(target=inner,args=args,kwargs=kwargs)
   res.thread=t
   return res

def myFun(v,debug=False):
  import time
  if debug: print "Debug mode ON"
  time.sleep(5)
  return v*2

x=threadwrap(myFun,[11],{"debug":True})
x.thread.start()
x.thread.join()
print x.result

Cela semble correct, et la classe threading.Thread semble être facilement étendue (*) avec ce genre de fonctionnalité, alors je me demande pourquoi elle n’est pas déjà là. Y at-il un défaut avec la méthode ci-dessus?

(*) Notez que la réponse de husanu à cette question fait exactement cela, en sous-classant threading.Thread, ce qui donne une version où join() donne la valeur de retour.

0
Andz