python监控函数超时_python装饰器:三种函数超时机制

本文介绍了三种使用Python装饰器实现函数超时的方法:1) 通过闹钟信号;2) 暴力kill线程;3) 优雅地关闭线程。这些机制在函数执行时间超过设定阈值时能自动中断函数执行,防止程序阻塞。
摘要由CSDN通过智能技术生成

python装饰器:三种函数超时机制

[问题]

在python中阻塞式地调用某个函数时,如果函数执行实行过长或者卡住是我们无法接受的,那么我们就需要一种机制:给函数设置一个“执行超时时间”,当函数执行时间超过设定的阈值时就自动退出。

[方案]

1.闹钟信号

[code lang=”python”]

#coding=utf-8

import functools

import signal

import threading

import time

class TimeOutException(Exception):

pass

def time_limit(timeout):

def wrapper(func):

def handle(signum, frame):

raise TimeOutException("running timeout!")

@functools.wraps(func)

def to_do(*args, **kwargs):

try:

signal.signal(signal.SIGALRM, handle)

signal.alarm(timeout) #开启闹钟信号

ret = func(*args, **kwargs)

signal.alarm(0) #关闭闹钟信号

return ret

except TimeOutException as e:

emsg="function(%s) execute timeout after %d second" % (func.__name__, timeout)

raise TimeOutException(emsg)

return to_do

return wrapper

def case_1():

print "—case_1—"

@time_limit(5)

def foo():

for sec in range(1, 10):

print "sec: %d" % sec

time.sleep(1)

try:

foo()

print ret

except Exception as e:

print e

def case_2():

print "—case_2—"

t =threading.Thread(target=case_1)

t.start()

t.join()

if __name__ == ‘__main__’:

case_1()

case_2()

[/code]

这种方案存在一个问题,信号机制只能在主线程中生效。

2.暴力kill线程

[code lang=”python”]

#coding=utf-8

import ctypes

import functools

import inspect

import signal

import threading

import time

class TimeOutException(Exception):

pass

def time_limit(timeout=0):

def decorator(func):

@functools.wraps(func)

def wrapper(*args, **kwargs):

def _async_raise(tid, exctype):

"""raises the exception, performs cleanup if needed"""

tid = ctypes.c_long(tid)

if not inspect.isclass(exctype):

exctype = type(exctype)

res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))

if res == 0:

raise ValueError("invalid thread id")

elif res != 1:

# """if it returns a number greater than one, you’re in trouble,

# and you should call it again with exc=NULL to revert the effect"""

ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)

raise SystemError("PyThreadState_SetAsyncExc failed")

class TimeLimit(threading.Thread):

def __init__(self):

super(TimeLimit, self).__init__()

self.error=None

self.result=None

def run(self):

try:

self.error=None

self.result=func(*args, **kwargs)

except Exception as e:

self.error = e

self.result = None

def stop(self):

try:

_async_raise(self.ident, SystemExit)

except Exception:

pass

t = TimeLimit()

t.setDaemon(True)

t.start()

if timeout > 0:

t.join(timeout)

else:

t.join()

if t.isAlive():

t.stop()

emsg="function(%s) execute timeout after %d second" % (func.__name__, timeout)

raise TimeOutException(emsg)

if t.error is not None:

raise t.error

return t.result

return wrapper

return decorator

def foo():

@time_limit(timeout=5)

def bar():

for sec in range(1, 10):

print "sec: %d" % sec

time.sleep(1)

try:

bar()

except Exception as e:

print e

if __name__ == "__main__":

foo()

[/code]

3.优雅地关闭线程

[code lang=”python”]

#coding=utf-8

import functools

import sys

import threading

import time

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

class TimeOutException(Exception):

pass

def time_limit(timeout):

def decorator(func):

def to_do(func, args, kwargs, result):

result.append(func(*args, **kwargs))

@functools.wraps(func)

def wapper(*args, **kwargs):

result = []

_kwargs = {

‘func’: func,

‘args’: args,

‘kwargs’: kwargs,

‘result’: result

}

t = KThread(target=to_do, args=(), kwargs=_kwargs)

t.start()

t.join(timeout)

if t.isAlive():

t.kill()

emsg="function(%s) execute timeout after %d second" % (func.__name__, timeout)

raise TimeOutException(emsg)

else:

return result[0]

return wapper

return decorator

def foo():

@time_limit(5)

def bar():

for sec in range(1, 10):

print "sec: %d" % sec

time.sleep(1)

try:

bar()

except Exception as e:

print e

if __name__ == ‘__main__’:

foo()

[/code]

About 小卒子

做一个好人,但不傻

Required fields are marked *

Comment

姓名 *

电子邮件 *

站点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值