Comment faire en sorte qu'un thread retourne un tuple ou toute valeur de mon choix au parent en Python?
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 .put
s 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! -)
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.
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.
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
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.
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()
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()
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
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
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
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)
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.