当前位置: 首页 > 科技观察

Linux-Mac下Python函数添加超时时间

时间:2023-03-22 13:42:03 科技观察

我们在使用requests等第三方库进行网络请求时,可以看到它有一个参数叫timeout,意思是当网络请求到时开始计算发布。如果没有收到返回,则抛出超时异常。(当然也有超时会失败的特殊情况,请看作者在Timeoutsandcancellationforhumans*[1]一文中的例子,我们不考虑这种特殊情况)。但是大家有没有想过普通函数怎么设置超时呢?特别是在运行一些数据处理和AI相关的代码时,一个函数可能会运行很长时间。我们要实现的是,当函数运行超出特定设定的时间时,会自动报错。比如有一个场景,我写了一个函数calc_statistic(datas),根据用户传入的数据计算出某个值。但是如果用户传入的数据非常大,这个函数可能会运行很长时间。我想将此功能设置为最多运行10秒。如果在10秒内没有完成操作,将会报错。我应该怎么办?如果你的电脑操作系统是linux或者macOS,可以使用signal来解决。在前几天的文章公众号中,我们介绍了使用signal来接管键盘的中断信号:《一日一技:在 Python 中接管键盘中断信号》,使用signal.SIGINT。今天我们将使用signal.SIGALRM。首先,让我们看看如何使用这个信号:importtimeimportsignaldefhandler(signum,_):print('It'stimed!')raiseException('It'stimed!')defclac_statistic(datas):time.sleep(100)signal.signal(signal.SIGALRM,handler)signal.alarm(5)clac_statistic('xxx')如下图:先将signal.SIGALRM事件绑定到handler函数,然后使用signal.alarm(10)发送延迟10秒的信号。10秒后,函数处理程序运行。函数中抛出异常,导致程序结束。clac_statistic函数本来运行了100秒,10秒后就停止了,从而实现了函数的超时功能。基于以上原理,我们实现了一个装饰器来简化不同函数的超时函数的设置:importtimeimportsignalclassFuncTimeoutException(Exception):passdefhandler(signum,_):raiseFuncTimeoutException('Thefunctionistimed!')deffunc_timeout(times=0):defdecorator(func):ifnottimes:returnfuncdefwraps(*args,**kwargs):signal.alarm(times)result=func(*args,**kwargs)signal.alarm(0)#函数提前运行,取消信号returnresultreturnwrapsreturndecoratorsignal.signal(signal.SIGALRM,handler)让我们尝试测试这个函数超时装饰器。首先,当测试函数的运行时间小于超时时间时,程序正常运行没有问题;然后测试函数运行时间超过超时时间的情况:正常抛出FuncTimeoutException。在实际使用中,我们可以使用try...exceptFuncTimeoutException来捕获这个异常,然后实现自定义的处理流程,例如:try:clac_statistic(100)exceptFuncTimeException:print('本函数超时,运行自定义处理Process')当然,如果想直接跳过这个异常也没关系,参考:importcontextlib:withcontextlib.supress(FuncTimeException):clac_statistic(100)