两种思路,一个是用signal,一个是多线程
先来说第一种思路,首先需要明确,signal只能用在主线程里,所以如果是非主程序中调用的函数,此方案不合适
import signal
import time
def set_timeout(num, callback):
def wrap(func):
def handle(signum, frame): # 收到信号 SIGALRM 后的回调函数,第一个参数是信号的数字,第二个参数是the interrupted stack frame.
raise RuntimeError
def to_do(*args, **kwargs):
try:
signal.signal(signal.SIGALRM, handle) # 设置信号和回调函数
signal.alarm(num) # 设置 num 秒的闹钟
print('start alarm signal.')
r = func(*args, **kwargs)
print('close alarm signal.')
signal.alarm(0) # 关闭闹钟
return r
except RuntimeError as e:
callback()
return to_do
return wrap
def after_timeout(): # 超时后的处理函数
print("Time out!")
@set_timeout(2, after_timeout) # 限时 2 秒超时
def connect(): # 要执行的函数
time.sleep(3) # 函数执行时间,写大于2的值,可测试超时
print('Finished without timeout.')
if __name__ == '__main__':
connect()
第二种,建立子线程监控函数执行状态,缺点是需要循环监控返回值,资源消耗较大
import time
import threading
class MyThread(threading.Thread):
def __init__(self, target, args=()):
"""
因为threading类没有返回值,因此在此处重新定义MyThread类,使线程拥有返回值
"""
super(MyThread, self).__init__()
self.func = target
self.args = args
def run(self):
# 接受返回值
self.result = self.func(*self.args)
def get_result(self):
# 线程不结束,返回值为None
try:
return self.result
except Exception:
return None
def limit_decor(timeout, granularity):
"""
timeout 最大允许执行时长, 单位:秒
granularity 轮询间隔,间隔越短结果越精确同时cpu负载越高
return 未超时返回被装饰函数返回值,超时则返回 None
"""
def functions(func):
def run(*args):
thre_func = MyThread(target=func, args=args)
thre_func.setDaemon(True)
thre_func.start()
sleep_num = int(timeout//granularity)
for i in range(0, sleep_num):
infor = thre_func.get_result()
if infor:
return infor
else:
time.sleep(granularity)
return None
return run
return functions
@limit_decor(2, 0.02)
def test_func():
time.sleep(3)
return 1
print(test_func())
其他解决方案也总结一下
- 使用eventlet ,测试未成功,可以自行测试
- 使用timeout-decorator,该工具就是封装了signal和多进程轮询的方法来达到终止程序的目的,缺点是相对于多线程资源消耗太大
https://segmentfault.com/q/1010000006090519?_ea=1016686
https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread