编程语言在 Linux/Mac 下为Python函数添加超时时间

    作者:kingname更新于: 2020-02-27 15:56:58

    大神带你学编程,欢迎选课

    Linux/Mac 下为Python函数添加超时时间。高级语言的出现使得计算机程序设计语言不再过度地依赖某种特定的机器或环境。这是因为高级语言在不同的平台上会被编译成不同的机器语言,而不是直接被机器执行。最早出现的编程语言之一FORTRAN的一个主要目标,就是实现平台独立。

    我们在使用 requests 这类网络请求第三方库时,可以看到它有一个参数叫做timeout,就是指在网络请求发出开始计算,如果超过 timeout 还没有收到返回,就抛出超时异常。(当然存在特殊情况timeout 会失效,请看Timeouts and cancellation for humans*[1] 这篇文章中作者的举例,我们不考虑这种特殊情况)。

    编程语言在 Linux/Mac 下为Python函数添加超时时间_操作系统视频_Python课程_Linux课程_课课家

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

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

    如果你的电脑操作系统是 Linux 或者 macOS,那么 可以使用 signal 来解决。

    在公众号前几天的文章中,我们介绍了使用signal来接管键盘的中断信号:《一日一技:在 Python 中接管键盘中断信号》,用到的是signal.SIGINT。今天我们要用到的是signal.SIGALRM。

    首先我们来看看这个信号的使用方法:

    1. import time 
    2. import signal 
    3.  
    4.  
    5. def handler(signum, _): 
    6.     print('定时到!'
    7.     raise Exception('定时到了!'
    8.  
    9. def clac_statistic(datas): 
    10.     time.sleep(100) 
    11.      
    12.  
    13. signal.signal(signal.SIGALRM, handler) 
    14. signal.alarm(5) 
    15. clac_statistic('xxx'

    运行效果如下图所示:

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

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

    1. import time 
    2. import signal 
    3.  
    4.  
    5. class FuncTimeoutException(Exception): 
    6.     pass 
    7.  
    8. def handler(signum, _): 
    9.     raise FuncTimeoutException('函数定时到了!'
    10.  
    11. def func_timeout(times=0): 
    12.     def decorator(func): 
    13.         if not times: 
    14.             return func 
    15.         def wraps(*args, **kwargs): 
    16.             signal.alarm(times) 
    17.             result = func(*args, **kwargs) 
    18.             signal.alarm(0)  # 函数提前运行完成,取消信号 
    19.             return result 
    20.         return wraps 
    21.     return decorator 
    22.  
    23. signal.signal(signal.SIGALRM, handler) 

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

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

    正常抛出FuncTimeoutException异常。

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

    1. try: 
    2.     clac_statistic(100) 
    3. except FuncTimeException: 
    4.     print('该函数运行超时,运行自定义的处理流程'

    当然你如果想直接跳过这个异常也没问题:

    1. import contextlib: 
    2. with contextlib.supress(FuncTimeException): 
    3.     clac_statistic(100) 
      在过去的几十年间,大量的编程语言被发明、被取代、被修改或组合在一起。尽管人们多次试图创造一种通用的程序设计语言,却没有一次尝试是成功的。之所以有那么多种不同的编程语言存在的原因是,编写程序的初衷其实也各不相同;新手与老手之间技术的差距非常大,而且有许多语言对新手来说太难学;还有,不同程序之间的运行成本(runtime cost)各不相同。

课课家教育

未登录