나는 kindall의 대답을 훔쳐서 조금 정리했습니다.
핵심 부분은 시간 초과를 처리하기 위해 * args 및 ** kwargs를 join ()에 추가하는 것입니다.
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
아래의 업데이트 된 답변
이것은 가장 인기가 높은 투표 답변이므로 py2와 py3 모두에서 실행될 코드로 업데이트하기로 결정했습니다.
또한 Thread.join ()에 대한 이해력이 부족하다는이 질문에 대한 많은 답변이 있습니다. 일부는 timeout
인수 를 완전히 처리하지 못합니다 . 그러나 (1) 반환 할 수있는 대상 함수가 None
있고 (2) timeout
arg를 join ()에 전달할 때 인스턴스에 대해 알아야 할 코너 케이스가 있습니다 . 이 코너 사례를 이해하려면 "테스트 4"를 참조하십시오.
py2 및 py3에서 작동하는 ThreadWithReturn 클래스 :
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
일부 샘플 테스트는 다음과 같습니다.
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))
테스트 4에서 발생할 수있는 코너 케이스를 식별 할 수 있습니까?
문제는 giveMe ()가 None을 리턴 할 것으로 예상하지만 (TEST 2 참조) join ()이 시간 초과되면 None을 리턴 할 것으로 예상합니다.
returned is None
다음 중 하나를 의미합니다.
(1) giveMe ()가 반환 한 것, 또는
(2) join () 시간 초과
giveMe ()가 항상 None을 반환한다는 것을 알기 때문에이 예제는 간단합니다. 그러나 실제 상황에서 (대상이 합법적으로 None 또는 다른 것을 반환 할 수있는 경우) 우리는 무슨 일이 있었는지 명시 적으로 확인하고 싶습니다.
다음은이 코너 케이스를 해결하는 방법입니다.
# 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]
주문이 유지되고 종료with
하면 결과 수집이 허용됩니다.[f.result() for f in futures]