Valor de retorno do thread
-
19-09-2019 - |
Pergunta
Como faço para obter um tópico para retornar uma tupla ou qualquer valor da minha escolha de volta ao pai em Python?
Solução
Eu sugiro que você instancie um Fila.queue Antes de iniciar o tópico, e passe como um dos args do fio: antes que o tópico termine, ele .put
é o resultado na fila que recebeu como argumento. O pai pode .get
ou .get_nowait
à vontade.
As filas são geralmente a melhor maneira de organizar a sincronização e a comunicação do encadeamento em Python: são veículos intrinsecamente seguros para roscas e que passem de mensagens-a melhor maneira de organizar multitarefa em geral!-)
Outras dicas
Se você estava ligando para ingressar () para aguardar a conclusão do thread, você pode simplesmente anexar o resultado à própria instância do thread e recuperá -lo do thread principal após o retorno da junção ().
Por outro lado, você não nos diz como pretende descobrir que o thread está feito e que o resultado está disponível. Se você já tem uma maneira de fazer isso, provavelmente o apontará (e nós, se você nos contar) para a melhor maneira de divulgar os resultados.
Você deve passar em uma instância da fila como um parâmetro, então deve .put () seu objeto de retorno na fila. Você pode reunir o valor de retorno via fila.get () qualquer objeto que você coloque.
Amostra:
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)
Use para vários 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()
Eu uso essa implementação e funciona muito bem para mim. Eu gostaria que você faça isso.
Usar Lambda Para embrulhar sua função de thread de destino e passar o valor de retorno de volta ao fio pai usando um fila. (Sua função de destino original permanece inalterada sem o parâmetro extra da fila.)
Código de amostra:
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())
Resultado:
4
Estou surpreso que ninguém tenha mencionado que você poderia simplesmente passar por isso:
>>> 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}
Talvez isso tenha grandes questões das quais não tenho conhecimento.
Outra abordagem é passar uma função de retorno de chamada para o thread. Isso fornece uma maneira simples, segura e flexível de retornar um valor ao pai, a qualquer momento do novo 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()
Você pode usar sincronizado fila módulo.
Considere que você precisa verificar um usuário Infos do banco de dados com um ID conhecido:
def check_infos(user_id, queue):
result = send_data(user_id)
queue.put(result)
Agora você pode obter seus dados assim:
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
Bem, no módulo de rosqueamento do Python, existem objetos de condição associados a bloqueios. Um método acquire()
retornará qualquer valor retornado do método subjacente. Para maiores informações: Objetos de condição de python
Baseado na sugestão do JCOMEAU_ICTX. O mais simples que encontrei. O requisito aqui era obter o status de saída STAUS de três processos diferentes em execução no servidor e acionar outro script se os três forem bem -sucedidos. Isso parece estar funcionando bem
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
A função de wrapper a seguir envolverá uma função existente e retornará um objeto que aponta para o tópico (para que você possa ligar start()
,join()
, etc. nele), bem como acessar/visualizar seu eventual valor de retorno.
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
Parece bom, e o threading.Thread
A classe parece ser facilmente estendida (*) com esse tipo de funcionalidade, por isso estou me perguntando por que já não está lá. Existe uma falha no método acima?
(*) Observe que a resposta de Husanu para esta pergunta faz exatamente isso, subclassificando threading.Thread
resultando em uma versão onde join()
fornece o valor de retorno.
Para programas fáceis, as respostas acima parecem um pouco como um exagero para mim. Eu envolveria a abordagem mutável:
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)