Python函数超时,用装饰器解决 func_timeout
python之为函数执行设置超时时间
python利用signal自定义函数超时机制
1 使用场景
我们在自定义一个函数后,会调用这个函数来完成我们想要的功能。就拿爬虫来举例,你发送请求,服务器给你响应,但是有可能服务器没有给你任何数据,无论是他识别了爬虫、还是服务器繁忙什么原因,这个时候,你的爬虫就会一直等待响应,这个时候就会非常浪费资源,还会造成程序阻塞。
(1)好在requests和scrapy有自定义timeout时间,例如:在requests中这样写:
requests.post(url, headers=headers,
data=data, proxies=proxies, timeout=15)
(2)在scrapy自定义下载超时时间DOWNLOAD_TIMEOUT = 15。
但是,以上所说的仅仅是爬虫,实际中还会有各种各样的情况。
2 安装使用
pip3 install func_timeout
pip3 install func_timeout-4.3.5.tar.gz
使用:在你的函数前加上装饰器,如下:
2.1 设置超时时间
from func_timeout import func_set_timeout
import func_timeout
import time
@func_set_timeout(3)
def task():
print('hello world')
time.sleep(5)
return '执行成功_未超时'
if __name__ == '__main__':
try:
print(task())
except func_timeout.exceptions.FunctionTimedOut:
print('执行函数超时')
func_timeout将在指定的参数的线程中运行指定的函数,直到返回,引发异常或超时。如果存在返回或异常,则将正常返回。
可以看到使用方法很简单,直接加上想要的超时时间即可。但是会抛出异常,终止你的程序。官方提供的捕获异常方法。
2.2 记录异常日志信息
from func_timeout import func_set_timeout
import func_timeout
import sys
# 配置日志信息
import logging
# (1)%(asctime)s日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896
# (2)%(levelname)s该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
# (3)%(filename)s pathname的文件名部分,包含文件后缀
# (4)%(message)s 日志记录的文本内容
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(filename)s - %(message)s"
logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT)
# logging.debug("This is a debug log.")
# logging.info("This is a info log.")
# logging.warning("This is a warning log.")
# logging.error("This is a error log.")
# logging.critical("This is a critical log.")
@func_set_timeout(3)
def main():
x = sys.stdin
# 从标准输入获取数据
for line in x:
print(line)
break
if __name__ == '__main__':
try:
main()
except func_timeout.exceptions.FunctionTimedOut:
logging.warning("执行函数超时")
3 signal的使用
只能用于linux环境。
import os
import signal
import sys
# 配置日志信息
import logging
# (1)%(asctime)s日志事件发生的时间--人类可读时间,如:2003-07-08 16:49:45,896
# (2)%(levelname)s该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
# (3)%(filename)s pathname的文件名部分,包含文件后缀
# (4)%(message)s 日志记录的文本内容
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(filename)s - %(message)s"
logging.basicConfig(filename='my.log', level=logging.DEBUG, format=LOG_FORMAT)
# logging.debug("This is a debug log.")
# logging.info("This is a info log.")
# logging.warning("This is a warning log.")
# logging.error("This is a error log.")
# logging.critical("This is a critical log.")
def main():
x = sys.stdin
# 从标准输入获取数据
for line in x:
print(line)
break
#定义信号处理函数
def myhandler(signum,frame):
# os.kill(os.getpid(),signal.SIGKILL)
logging.warning("执行函数超时")
exit() # 终止Python程序
if __name__ == '__main__':
try:
signal.signal(signal.SIGALRM,myhandler) # 注册 signal.SIGALRM's handler
signal.alarm(10) # 10秒后向进程发送SIGALRM信号
main()
signal.alarm(0) # 函数在规定时间执行完后关闭alarm闹钟
except Exception as e:
logging.error(e)