前言:
(不想看废话的可以直接copy尾部的代码)
在八月上旬的时候,曾经写过一个多线程爬虫。程序在运行时经常莫名的卡死。这令我很是费解,后来才发现,是在请求对方资源时,服务器长时间未返回完数据。导致IO阻塞。
其实不只是爬虫,很多时候一个函数很可能因为某种不可预知的事情,而有时很可能会卡在某一处,继而函数无法继续执行下去。导致拥塞。
此时,我们自然而然的会想到如果能写一个装饰器来限制一个函数执行的时间,那么问题也就迎刃而解了。
正文:
最初,因为我的拥塞是发生在多进程中,我曾经想通过杀死线程的方式来解决问题。然而python中的threading模块中并没有提供这一方法。网上有类似方法,但是比较繁琐。并且非常不建议直接杀死线程(这其中可能关系到资源释放的问题,比如我从队列中取出了一个url, 然而我的进程卡死了,并没有完成相应的任务,我却强行得终止了这个线程,url中的内容未采集,url也没用放回到队列中,岂不是造成了资源的浪费)。后来想到可以直接对函数的运行时间进行限制,这样就省了不少事情。
方法一:
此方法比较容易理解,写了一个装饰器,看到其中的__wrapper函数,新建了一个守护线程,target是我们需要限制时间的函数。启动守护线程之后,主线程sleep(timer)(timer就是我们设定的函数运行时间)。若在规定的运行时间未结束守护进程(也就是我们需要限制运行时间的函数),则主动抛出异常。(但此种方法有一个弊端,就是如果函数在目标时间内运行完了,由于sleep的原因,整个线程还是会等到sleep(timer)后结束,可能会浪费时间)
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#Author: Chen
import threading
import time
def time_limited(timer):
'''
一个规定函数执行时间的装饰器
:param timer:
:return:
'''
def wrapper(func):
def __wrapper(params):
start_time = time.time()
#通过设置守护线程强制规定函数的运行时间
t = threading.Thread(target=func, args=params)
t.setDaemon(True)
t.start()
time.sleep(timer)
if t.is_alive():
#若在规定的运行时间未结束守护进程,则主动抛出异常
raise Exception('Function execution timeout')
#print time.time()-start_time
return __wrapper
return wrapper
第二种:
此种方法是第一种的升级,改进了函数运行完不结束的弊端
#!/usr/bin/env python
#-*- coding:utf-8 -*-
#Author: Chen
import time
from threading import Thread
ThreadStop = Thread._Thread__stop#获取私有函数
def time_limited_pri(time_limited):
def wrapper(func):
def __wrapper(params):
class TimeLimited(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
func(params)
def _stop(self):
if self.is_alive():
ThreadStop(self)
raise Exception('Function execution overtime')
t = TimeLimited()
t.start()
t.join(timeout=time_limited)
if t.is_alive():
t._stop()
raise Exception('Function execution overtime')
return __wrapper
return wrapper