在 Linux/Mac 下为Python函数添加超时时间

咱们在使用 requests 这类网络请求第三方库时,能够看到它有一个参数叫作timeout,就是指在网络请求发出开始计算,若是超过 timeout 尚未收到返回,就抛出超时异常。(固然存在特殊状况timeout 会失效,请看Timeouts and cancellation for humans* 这篇文章中做者的举例,咱们不考虑这种特殊状况)。python

但你们有没有考虑过,如何为普通的函数设置超时时间?特别是在运行一些数据处理、AI 相关的代码时,某个函数可能会运行很长时间,咱们想实现,在函数运行超过特定的时间时,自动报错。网络

例若有这样一个场景,我写了一个函数calc_statistic(datas),根据用户传入的数据计算某个值。但若是用户传入的数据很是大,这个函数就可能运行很长时间。我想设置让这个函数最多运行10秒钟。若是10秒尚未运行完成,就报错。应该怎么办呢?函数

若是你的电脑操做系统是 Linux 或者 macOS,那么 可使用 signal 来解决。测试

在公众号前几天的文章中,咱们介绍了使用signal来接管键盘的中断信号,用到的是signal.SIGINT。今天咱们要用到的是signal.SIGALRMspa

首先咱们来看看这个信号的使用方法:操作系统

import time
import signal


def handler(signum, _):
    print('定时到!')
    raise Exception('定时到了!')

def clac_statistic(datas):
    time.sleep(100)
    

signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
clac_statistic('xxx')
复制代码

运行效果以下图所示:code

首先绑定signal.SIGALRM事件到handler函数中,而后使用signal.alarm(10)延迟10秒发送一个信号。10秒到了之后,函数handler被运行。在函数中抛出了一个异常,致使程序结束。clac_statistic函数本来要运行100秒,可是在10秒之后就中止了,从而实现了函数的超时功能。cdn

基于以上原理,咱们实现一个装饰器,来简化为不一样函数设置超时功能:blog

import time
import signal


class FuncTimeoutException(Exception):
    pass

def handler(signum, _):
    raise FuncTimeoutException('函数定时到了!')

def func_timeout(times=0):
    def decorator(func):
        if not times:
            return func
        def wraps(*args, **kwargs):
            signal.alarm(times)
            result = func(*args, **kwargs)
            signal.alarm(0)  # 函数提早运行完成,取消信号
            return result
        return wraps
    return decorator

signal.signal(signal.SIGALRM, handler)
复制代码

咱们来试一试测试一下这个函数超时装饰器。首先测试函数的运行时间小于超时时间时,程序正常运行没有问题:事件

再来测试一下函数运行时间超过超时时间的状况:

正常抛出FuncTimeoutException异常。

那咱们在实际使用中,可使用try...except FuncTimeoutException捕获这个异常,而后实现自定义的处理流程,例如:

try:
    clac_statistic(100)
except FuncTimeException:
    print('该函数运行超时,运行自定义的处理流程')
复制代码

固然你若是想直接跳过这个异常也没问题:

import contextlib:
with contextlib.supress(FuncTimeException):
    clac_statistic(100)
复制代码

相关文章
相关标签/搜索