我偷了 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 arg。但是还有一个极端情况,当您有 (1) 一个可以返回 None 的目标函数和 (2) 您还将 timeout arg 传递给 join() 时,您应该注意有关实例的极端情况。请参阅“测试 4”以了解这种极端情况。
适用于 py2 和 py3 的 ThreadWithReturn 类:
import sys
from threading import Thread
from builtins import super # https://*.com/a/30159479
_thread_target_key, _thread_args_key, _thread_kwargs_key = (
('_target', '_args', '_kwargs')
if sys.version_info >= (3, 0) else
('_Thread__target', '_Thread__args', '_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 target is not 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))
您能确定我们在 TEST 4 中可能遇到的极端情况吗?
问题是我们希望 giveMe() 返回 None(参见测试 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()