Python 的自定义超时机制 - 装饰器的妙用
装饰器
关于装饰器的入门,可以参考这篇文章:12步轻松搞定python装饰器
简单来说,装饰器其实就是一个闭包(闭包 – 被函数记住的封闭作用域 – 能够被用来创建自定义的函数),把一个函数当做参数然后返回一个替代版的函数,在Python里面,函数也是对象。实际上装饰器就是一个加强的函数,目的是为了更简洁地加强函数的同时并且不影响函数的内部。
那么我们想实现超时中止机制的时候,是不是可以考虑给某函数func增加一个装饰器,让装饰器实现超时中止的功能,这样还能把装饰器用在其他的函数里面,同时我们的func函数的内部也不会有额外的代码。
有空会补充装饰器的细节,或者另开一篇文章。
信号
在实现超时中止的装饰器之前,先引入一下Python的信号(signal)机制。
信号机制的作用:发送和接收异步系统信号
信号是一个操作系统特性,它提供了一个途径可以通知程序发生了一个事件并异步处理这个事件。信号可以由系统本身生成,也可以从一个进程发送到另一个进程。
信号实际上是进程之间通讯的方式,是一种软件中断。一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号。
Python标准库中的signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂停并等待信号,以及定时发出SIGALRM等。要注意,signal包主要是针对UNIX平台(比如Linux, MAC OS),而Windows内核中由于对信号机制的支持不充分,所以在Windows上的Python不能发挥信号系统的功能。
下面简单介绍一下这次会用到的signal包的相关方法,更多的细节可以参考:
signal包的核心是使用signal.signal()函数来预设(register)信号处理函数,如下所示:
import signal
'''
python 的信号名与Linux系统一致,
查看你的linux支持哪些信号:kill -l 即可
SIGINT 终止进程 中断进程 (control+c)
SIGTERM 终止进程 软件终止信号
SIGKILL 终止进程 杀死进程
SIGALRM 闹钟信号
'''
signal.signal(signal_num, handler)
'''
signal_num为某个信号,handler为该信号的处理函数,或者说是回调函数。
进程可以无视信号,可以采取默认操作,还可以自定义操作。
当handler为signal.SIG_IGN时,信号被无视(ignore)。
当handler为singal.SIG_DFL,进程采取默认操作(default)。
当handler为一个函数名时,进程采取函数中定义的操作。
'''
signal.alarm(seconds) #如果time 非0,这个函数则响应一个SIGALRM信号并在time秒后发送到该进程。
signal.alarm(0) #假如在callback函数未执行的时候,要取消的话,那么可以使用alarm(0)来取消调用该回调函数
实现
有了上面的信号基础,我们就可以用signal.alarm(t)来做超时中止,实现思路简单来说就是,在执行某函数func前,设定好一个seconds时间的闹钟信号signal.alarm(t)。这也就是意味着如果超时(也就是t秒后),会有一个信号激发信号的回调函数handler,这样只要回调函数可以引发异常就可以实现超时中止功能了;如果没有超时,则在func运行结束后使用signal.alarm(0) 取消回调函数handler的执行。
import signal,functools
class TimeoutError(Exception):pass #定义一个超时错误类
def time_out(seconds,error_msg='TIME_OUT_ERROR:No connection were found in limited time!'):
#带参数的装饰器
def decorated(func):
result = ''
def signal_handler(signal_num,frame): # 信号机制的回调函数,signal_num即为信号,frame为被信号中断那一时刻的栈帧
global result
result = error_msg
raise TimeoutError(error_msg) #raise显式地引发异常。一旦执行了raise语句,raise后面的语句将不能执行
def wrapper(*args,**kwargs): #def wrapper(func,*args,**kwargs):
global result
signal.signal(signal.SIGALRM, signal_handler)
signal.alarm(seconds) #如果time是非0,这个函数则响应一个SIGALRM信号并在time秒后发送到该进程。
try:
result = func(*args,**kwargs)
#若超时,此时alarm会发送信息激活回调函数signal_handler,从而引发异常终止掉try的代码块
finally:
signal.alarm(0) #假如在callback函数未执行的时候,要取消的话,那么可以使用alarm(0)来取消调用该回调函数
print('finish')
return result
return functools.wraps(func)(wrapper) #return wrapper
return decorated
import time
@time_out(5) #给func设定了超时时间为5s
def func():
#可以插入http请求等代码
time.sleep(10) #模拟超时
return
#调用func
func()
后话
超时中止机制还可以配合threading等实现超时kill的效果
比如:论 Python 装饰器控制函数 Timeout 的正确姿势
参考文章:
Understanding Python Decorators in 12 Easy Steps!
12步轻松搞定python装饰器
深入浅出 Python 装饰器:16 步轻松搞定 Python 装饰器
深入浅出 Python 装饰器:16 步轻松搞定 Python 装饰器
Python模块之信号signal
论 Python 装饰器控制函数 Timeout 的正确姿势
python装饰器:三种函数超时机制
推荐阅读
-
【2022新手指南】Java编程进阶之路 - 六、技术架构篇 ### MySQL索引底层解析与优化实战 - 你会讲解MySQL索引的数据结构吗?性能调优技巧知多少? - Redis深度揭秘:你知道多少?从基础到哨兵、主从复制全梳理 - Redis持久化及哨兵模式详解,还有集群搭建和Leader选举黑箱打开 - Zookeeper是个啥?特性和应用场景大公开 - ZooKeeper集群搭建攻略及 Leader选举、读写一致性、共享锁实现细节 - 探究ZooKeeper中的Leader选举机制及其在分布式环境中的作用 - Zab协议深入剖析:原理、功能与在Zookeeper中的核心地位 - RabbitMQ全方位解读:工作模式、消费限流、可靠投递与配置策略 - 设计者视角:RabbitMQ过期时间、死信队列与延时队列实践指南 - RocketMQ特性和应用场景揭示:理解其精髓与差异化优势 - Kafka详细介绍:特性及广泛应用于实时数据处理的场景解析 - ElasticSearch实力揭秘:特性概述与作为搜索引擎的广泛应用 - MongoDB认知升级:非关系型数据库的优势阐述,安装与使用实战教学 - BIO/NIO/AIO网络模型对比:掌握它们的区别与在网络编程中的实际应用 - Netty带你飞:理解其超快速度背后的秘密,包括线程模型分析 - 网络通信黑科技:Netty编解码原理与常用编解码器的应用,Protostuff实战演示 - 解密Netty粘包与拆包现象,怎样有效应对这一常见问题 - 自定义Netty心跳检测机制,轻松调整检测间隔时间的艺术 - Dubbo轻骑兵介绍:核心特性概览,服务降级实战与其实现益处 - Dubbo三大神器解读:本地存根与本地伪装的实战运用与优势呈现 ----------------------- 七、结语与回顾
-
深入理解Python装饰器的全貌详析
-
python 装饰器和传递参数的函数
-
为懒人设计的五个高效 Python 装饰器
-
Python 装饰器的作用/意义
-
推荐 8 个很酷的 Python 装饰器!
-
Python 装饰器:优雅而强大的代码增强工具
-
Python 中的装饰器介绍
-
用通俗易懂的语言解释 python 装饰器
-
python 使用装饰器和线程来限制函数的执行时间