Roubei a resposta de kindall e a limpei um pouco.
A parte principal é adicionar * args e ** kwargs a join () para lidar com o tempo limite
class threadWithReturn(Thread):
def __init__(self, *args, **kwargs):
super(threadWithReturn, self).__init__(*args, **kwargs)
self._return = None
def run(self):
if self._Thread__target is not None:
self._return = self._Thread__target(*self._Thread__args, **self._Thread__kwargs)
def join(self, *args, **kwargs):
super(threadWithReturn, self).join(*args, **kwargs)
return self._return
RESPOSTA ATUALIZADA ABAIXO
Esta é a minha resposta mais votada popularmente, por isso decidi atualizar com o código que será executado nos py2 e py3.
Além disso, vejo muitas respostas a essa pergunta que mostram uma falta de compreensão sobre o Thread.join (). Alguns falham completamente ao lidar com o timeoutargumento. Mas há também um caso em que você deve estar ciente das instâncias em que possui (1) uma função de destino que pode retornar Nonee (2) você também passa otimeout argumento para entrar em (). Consulte "TESTE 4" para entender este caso de canto.
Classe ThreadWithReturn que funciona com py2 e py3:
import sys
from threading import Thread
from builtins import super # https://stackoverflow.com/a/30159479
if sys.version_info >= (3, 0):
_thread_target_key = '_target'
_thread_args_key = '_args'
_thread_kwargs_key = '_kwargs'
else:
_thread_target_key = '_Thread__target'
_thread_args_key = '_Thread__args'
_thread_kwargs_key = '_Thread__kwargs'
class ThreadWithReturn(Thread):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._return = None
def run(self):
target = getattr(self, _thread_target_key)
if not target is None:
self._return = target(
*getattr(self, _thread_args_key),
**getattr(self, _thread_kwargs_key)
)
def join(self, *args, **kwargs):
super().join(*args, **kwargs)
return self._return
Alguns exemplos de testes são mostrados abaixo:
import time, random
# TEST TARGET FUNCTION
def giveMe(arg, seconds=None):
if not seconds is None:
time.sleep(seconds)
return arg
# TEST 1
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',))
my_thread.start()
returned = my_thread.join()
# (returned == 'stringy')
# TEST 2
my_thread = ThreadWithReturn(target=giveMe, args=(None,))
my_thread.start()
returned = my_thread.join()
# (returned is None)
# TEST 3
my_thread = ThreadWithReturn(target=giveMe, args=('stringy',), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=2)
# (returned is None) # because join() timed out before giveMe() finished
# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))
Você consegue identificar a caixa de canto que podemos encontrar com o TESTE 4?
O problema é que esperamos que o giveMe () retorne None (consulte TEST 2), mas também esperamos que o join () retorne None se o tempo limite exceder.
returned is None significa:
(1) foi isso que giveMe () retornou, ou
(2) tempo limite de participação () expirado
Este exemplo é trivial, pois sabemos que giveMe () sempre retornará None. Mas, no caso do mundo real (onde o destino pode legitimamente retornar Nenhum ou outra coisa), gostaríamos de verificar explicitamente o que aconteceu.
Abaixo está como lidar com este caso de canto:
# TEST 4
my_thread = ThreadWithReturn(target=giveMe, args=(None,), kwargs={'seconds': 5})
my_thread.start()
returned = my_thread.join(timeout=random.randint(1, 10))
if my_thread.isAlive():
# returned is None because join() timed out
# this also means that giveMe() is still running in the background
pass
# handle this based on your app's logic
else:
# join() is finished, and so is giveMe()
# BUT we could also be in a race condition, so we need to update returned, just in case
returned = my_thread.join()
futures = [executor.submit(foo, param) for param in param_list]A ordem será mantida e sair dawithpermitirá a coleta de resultados.[f.result() for f in futures]