我发现了一个创建超时函数here的代码,它似乎不起作用。完整的测试代码如下:
def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None):
import threading
class InterruptableThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.result = None
def run(self):
try:
self.result = func(*args, **kwargs)
except:
self.result = default
it = InterruptableThread()
it.start()
it.join(timeout_duration)
if it.isAlive():
return default
else:
return it.result
def foo():
while True:
pass
timeout(foo,timeout_duration=3)预期行为:代码在3秒内结束。问题出在哪里?
发布于 2012-12-11 22:53:15
一个线程不能优雅地终止另一个线程,所以使用您当前的代码,foo永远不会终止。(使用thread.daemon = True,Python程序将在只剩下守护线程时退出,但这不允许您在不终止主线程的情况下终止foo。)
Some people曾试图使用信号停止执行,但在某些情况下这可能是unsafe。
如果您可以修改foo,则有许多可能的解决方案。例如,您可以检查一个threading.Event以跳出while循环。
但是,如果您不能修改foo,您可以使用multiprocessing模块在子进程中运行它,因为与线程不同,子进程可以终止。下面是一个示例,说明了它可能是什么样子:
import time
import multiprocessing as mp
def foo(x = 1):
cnt = 1
while True:
time.sleep(1)
print(x, cnt)
cnt += 1
def timeout(func, args = (), kwds = {}, timeout = 1, default = None):
pool = mp.Pool(processes = 1)
result = pool.apply_async(func, args = args, kwds = kwds)
try:
val = result.get(timeout = timeout)
except mp.TimeoutError:
pool.terminate()
return default
else:
pool.close()
pool.join()
return val
if __name__ == '__main__':
print(timeout(foo, kwds = {'x': 'Hi'}, timeout = 3, default = 'Bye'))
print(timeout(foo, args = (2,), timeout = 2, default = 'Sayonara'))收益率
('Hi', 1)
('Hi', 2)
('Hi', 3)
Bye
(2, 1)
(2, 2)
Sayonara请注意,这也有一些限制。
func需要修改变量,您将需要使用shared variable.args传递),并且关键字(kwds)必须是线程,它们比线程更消耗资源。通常,您只想在程序开始时创建一次多进程池。此timeout函数在您每次调用它时都会创建一个Pool。这是必要的,因为我们需要pool.terminate()来终止foo。也许还有更好的办法,但我还没想到。--发布于 2012-12-11 21:13:46
您需要将it转换为daemon thread
it = ...
it.daemon = True
it.start()否则,它将被创建为用户线程,并且在所有用户线程完成之前,该进程不会停止。
请注意,在您的实现中,即使在等待线程超时之后,线程仍将继续运行并消耗资源。CPython的Global Interpreter Lock可能会进一步加剧这个问题。
发布于 2020-07-06 17:14:55
使用multiprocessing的好处是进程不共享内存,子进程中发生的任何事情都仅限于该函数,不会导致其他进程终止。将子进程的超时时间添加到3s的最简单方法是:
import multiprocessing
def my_child():
function here
process = multiprocessing.Process(target=my_child)
process.daemon = True
process.start()
process.join(3)
if process.is_alive():
process.terminate()https://stackoverflow.com/questions/13821156
复制相似问题