目标:实现一个超时自动切换当前任务到其他任务装饰器,然后还可以在其他任务完成后再切换回来的程序
原计划在signal.signal的handler中触发TimeoutError,然后在用户代码的exception中捕获到异常,此时就可以切换到其他任务了。如下:
import signal
import time
def timeout(seconds):
def raise_timeout(func):
def timeout_handler(signum, frame):
signal.alarm(seconds)
raise TimeoutError("timeout: {} seconds".format(seconds))
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(seconds)
return func
return raise_timeout
@timeout(5)
def loop_print():
count = 0
while True:
try:
count += 1
print("current times: {}".format(count))
time.sleep(1)
except TimeoutError as e:
print(e)
if __name__ == '__main__':
loop_print()
以上实现可以满足超时自动退出需求。
但如果把loop_print中的休眠调整到10s,就发现"never come back"永远不会输出,因为signal总是会提前切换到timeout_handler,触发TimeoutError,导致time.sleep执行一半就跳到异常,所以print(“never come back”)永远无法执行
@timeout(5)
def loop_print():
count = 0
while True:
try:
count += 1
print("current times: {}".format(count))
time.sleep(10)
print("never come back")
except TimeoutError as e:
print(e)
所以在handler中raise异常是不科学的,最终代码改为:
import signal
import time
def timeout(seconds, other_thing):
def _wapper(func):
def timeout_handler(signum, frame):
other_thing()
signal.alarm(seconds)
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(seconds)
return func
return _wapper
def do_other_thing():
print("other thing done")
@timeout(5, other_thing=do_other_thing)
def loop_print():
count = 0
while True:
count += 1
print("total seconds: {}".format(count))
for i in range(7):
time.sleep(1)
print(i)
print("never come back")
if __name__ == '__main__':
loop_print()
以上代码在执行完do_other_thing后仍然会接着之前继续sleep剩余的秒数,然后执行print(“never come back”),没有破坏业务代码的逻辑(loop_print使用sleep模拟业务处理)
线程中可以通过class threading.Timer(interval, function, args=None, kwargs=None)实现一下功能