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

开发 后端
我们在使用 requests 这类网络请求第三方库时,可以看到它有一个参数叫做timeout,就是指在网络请求发出开始计算,如果超过 timeout 还没有收到返回,就抛出超时异常。

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

[[315558]]

但大家有没有考虑过,如何为普通的函数设置超时时间?特别是在运行一些数据处理、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('该函数运行超时,运行自定义的处理流程'

当然你如果想直接跳过这个异常也没问题,参考《一日一技:不使用 try...except 掩盖一些已知异常》

 

  1. import contextlib: 
  2. with contextlib.supress(FuncTimeException): 
  3.     clac_statistic(100) 

 

责任编辑:华轩 来源: 未闻Code
相关推荐

2009-03-05 09:18:13

LinuxUbuntuMySQL

2009-07-06 18:09:18

linuxApache密码

2023-03-01 10:42:58

gRPC服务端设置

2016-09-19 09:15:49

Windows 10锁屏超时

2023-10-12 08:54:20

Spring事务设置

2023-08-14 21:54:41

OptionsFeign接口

2024-09-10 09:05:12

SpringREST并发

2019-07-22 15:20:40

Linux系统病毒

2023-11-29 07:40:12

分布式

2010-03-03 17:14:10

Linux路由设置ip

2009-04-15 20:52:37

Linux设置IPMAC绑定

2010-04-21 13:21:24

Oracle时间

2024-02-28 12:41:00

源码内核参数

2014-05-22 15:38:27

Android消息处理机制Looper

2024-03-29 00:00:00

JSAPI网络

2009-12-09 10:16:39

ibmdwLotus

2011-11-17 09:17:04

开发LinuxMacOS

2022-03-03 13:22:52

LinuxKDE PlasmaKDE

2022-11-03 11:19:22

2018-10-20 10:50:36

LinuxMac操作系统
点赞
收藏

51CTO技术栈公众号