Valeur de retour de fil
-
19-09-2019 - |
Question
Comment puis-je obtenir un fil pour retourner un tuple ou d'une valeur de mon choix au parent en Python?
La solution
Je vous suggère de instancier un Queue.Queue avant de commencer le fil, et le transmettre en tant que l'un des arguments du fil: avant que le fil se termine, il .put
s le résultat sur la file d'attente reçu comme argument. Le parent peut .get
ou .get_nowait
à volonté.
sont généralement Queues la meilleure façon d'organiser la synchronisation des threads et de la communication en Python: ils sont intrinsèquement thread-safe, les véhicules de passage de messages - la meilleure façon d'organiser multi-tâches en général -)
Autres conseils
Si vous appeliez joindre () pour attendre le fil pour terminer, vous pouvez simplement joindre le résultat à l'instance de fil lui-même, puis le récupérer à partir du thread principal après le retour join ().
Par contre, vous ne nous dites pas comment vous avez l'intention de découvrir que le fil est fait et que le résultat est disponible. Si vous avez déjà une façon de le faire, il pointera probablement vous (et nous, si vous deviez nous dire) à la meilleure façon de faire connaître les résultats.
Vous devez passer une instance de file d'attente comme paramètre alors vous devriez .put () votre objet de retour dans la file d'attente. Vous pouvez recueillir la valeur de retour via queue.get () quel que soit l'objet que vous mettez.
Exemple:
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)
L'utilisation de 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 mise en œuvre et il fonctionne très bien pour moi. Je vous souhaite le faire.
Utilisation lambda pour envelopper la fonction thread cible et passer sa valeur de retour vers le thread parent en utilisant une file d'attente . (Votre fonction cible originale 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 a mentionné que vous pouvez 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 cela a des problèmes majeurs dont je ne connais pas.
Une autre approche consiste à passer une fonction de rappel du fil. Cela donne un moyen simple, sûr et flexible pour retourner une valeur au parent, à tout moment 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 file d'attente le module .
Pensez vous devez vérifier un utilisateur infos de 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 __name__ == '__main__':
N = 100
arr = [ random.randint( 0, 100 ) for x in range( N ) ]
th = myThread( arr )
th.start( )
sortedArr = th.join( )
print "arr2: ", sortedArr
Eh bien, dans le module Python filetage, il y a des objets de l'état qui sont associés aux serrures. Une méthode acquire()
retourne la valeur retournée par la méthode sous-jacente. Pour plus d'informations: Condition Python objets
Basé sur la suggestion de jcomeau_ictx. Celui Je suis venu plus simple à travers. Exigence ici était d'obtenir le statut de sortie de trois processus Staus différents en cours d'exécution sur le serveur et déclencher un autre script si les trois réussissent. Cela semble fonctionner très bien
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
La fonction d'emballage suivant enveloppera une fonction existante et retourner un objet qui pointe à la fois sur le fil (de sorte que vous pouvez appeler start()
, join()
, etc. sur elle), ainsi que l'accès / voir sa valeur de retour éventuel.
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
Il semble OK, et la classe threading.Thread
semble être facilement étendu (*) avec ce type de fonctionnalité, alors je me demande pourquoi il n'y est pas déjà. Y at-il un défaut avec la méthode ci-dessus?
(*) Notez que la réponse à cette question Husanu exactement ce que fait, le sous-classement threading.Thread
résultant dans une version où join()
donne la valeur de retour.
Pour les programmes faciles les answeres ci-dessus ressemblent un peu exagéré pour moi. Je en-nicen 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)