Python tips: 超时装饰器, @timeout decorator

最近工作有点多,趁周末有空,继续分享我在学习和使用python过程中的一些小tips。

有没有遇到过这样的事情:对数据库执行插入或更新操作,因为数据量大或其他原因,导致此次操作非常耗时,有时甚至等上好几个小时,也无法完成。很郁闷,怎么操作不超时啊?因为数据库配置时超时时间很长,并且有些操作又是需要很长时间的,所以不能修改默认的超时时间。

因为客观条件不允许,我们不能靠数据库超时来终止此次操作,所以必须要在自己的方法逻辑模块里实现超时检测的功能。

在python里有没有可以不用修改原来的方法内部逻辑,就能实现超时检测呢?肯定有啦,就是利用装饰器。装饰器是什么?在博客园找到了一篇介绍文章:函数和方法装饰漫谈(Function decorator)

废话听完,我现在介绍主角出场:超时装饰器,timeout decorator。

超时检测逻辑:启动新子线程执行指定的方法,主线程等待子线程的运行结果,若在指定时间内子线程还未执行完毕,则判断为超时,抛出超时异常,并杀掉子线程;否则未超时,返回子线程所执行的方法的返回值。

在实现过程中,发现python默认模块里是没有方法可以杀掉线程的,怎么办呢?当然先问问google或百度,果然,keill thread这个关键词很热门,很快就搜索到我想要的东西了:"Kill a thread in Python",就是以下这个KThread类,它继承了threading.Thread,并添加了kill方法,让我们能杀掉它:

 

import  sys


class  KThread(threading.Thread):
    
""" A subclass of threading.Thread, with a kill()
    method.
    
    Come from:
    Kill a thread in Python: 
    http://mail.python.org/pipermail/python-list/2004-May/260937.html
    
"""
    
def   __init__ (self,  * args,  ** kwargs):
        threading.Thread.
__init__ (self,  * args,  ** kwargs)
        self.killed 
=  False

    
def  start(self):
        
""" Start the thread. """
        self.
__run_backup   =  self.run
        self.run 
=  self. __run        #  Force the Thread to install our trace.
        threading.Thread.start(self)

    
def   __run (self):
        
""" Hacked run function, which installs the
        trace.
"""
        sys.settrace(self.globaltrace)
        self.
__run_backup ()
        self.run 
=  self. __run_backup

    
def  globaltrace(self, frame, why, arg):
        
if  why  ==   ' call ' :
          
return  self.localtrace
        
else :
          
return  None

    
def  localtrace(self, frame, why, arg):
        
if  self.killed:
          
if  why  ==   ' line ' :
            
raise  SystemExit()
        
return  self.localtrace

    
def  kill(self):
        self.killed 
=  True

 

好了,万事戒备,让我们来完成剩下的代码吧,也就是timeout decorator:

class  Timeout(Exception):
    
""" function run timeout """
    
def  timeout(seconds):
    
""" 超时装饰器,指定超时时间
    若被装饰的方法在指定的时间内未返回,则抛出Timeout异常
"""
    
def  timeout_decorator(func):
        
""" 真正的装饰器 """
        
        
def  _new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):
            result.append(oldfunc(
* oldfunc_args,  ** oldfunc_kwargs))
        
        
def  _( * args,  ** kwargs):
            result 
=  []
            new_kwargs 
=  {  #  create new args for _new_func, because we want to get the func return val to result list
                 ' oldfunc ' : func,
                
' result ' : result,
                
' oldfunc_args ' : args,
                
' oldfunc_kwargs ' : kwargs
            }
            thd 
=  KThread(target = _new_func, args = (), kwargs = new_kwargs)
            thd.start()
            thd.join(seconds)
            alive 
=  thd.isAlive()
            thd.kill() 
#  kill the child thread
             if  alive:
                
raise  Timeout(u ' function run too long, timeout %d seconds. '   %  seconds)
            
else :
                
return  result[0]
        _.
__name__   =  func. __name__
        _.
__doc__   =  func. __doc__
        
return  _
    
return  timeout_decorator

 

真的能运行吗?写个测试程序运行运行:

@timeout( 5 )
def  method_timeout(seconds, text):
    
print   ' start ' , seconds, text
    time.sleep(seconds)
    
print   ' finish ' , seconds, text
    
return  seconds

if   __name__   ==   ' __main__ ' :
    
for  sec  in  range( 1 10 ):
        
try :
            
print   ' * '   *   20
            
print  method_timeout(sec,  ' test waiting %d seconds '   %  sec)
        
except  Timeout, e:
            
print  e

 

看,真的行:

 timeout_decorator.jpg

 

原始代码: threadutil.py.zip

 

本次的tips可能有点复杂,运用到多线程,装饰器等,希望它对你有所帮助。

PS:山顶风光果然一流,独缺美女相伴!!!223853735.gif

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值