python函数超时自动退出_python 函数超时停止装饰器

实用的例子

time.sleep 单线程阻塞延时

import time

def time_sleep():

for i in range(10):

print i

time.sleep(1) # delay 1s, not that accurate

if __name__ == "__main__":

start = time.time()

time_sleep()

end = time.time()

print "run time: {}".format(end - start)

time.time 单线程非阻塞延时/超时

通过比较时间戳实现, 多用于循环中的延时/超时判断

import time

def time_compare():

timeout = time.time() + 10 # 10s delay

for i in range(20):

print i

time.sleep(1)

if timeout < time.time(): # compare the timestamps

break

print "time out !"

if __name__ == "__main__":

start = time.time()

time_compare()

end = time.time()

print "run time: {}".format(end - start)

threading.Timer 多线程非阻塞延时

这个例子中, 会先执行完 threading_main. 5s后, 才会执行 threading_sub

子线程函数可以带参 threading.Timer(interval, function, args=[], kwargs={})

import threading

def threading_main():

print "main thread: start"

thrd = threading.Timer(5.0, threading_sub, args = ["sub thread"])

thrd.start()

print "main thread: end"

def threading_sub(name):

print name + ": hello"

if __name__ == "__main__":

start = time.time()

threading_main()

end = time.time()

print "run time: {}".format(end - start)

threading.Timer + threading.join 多线程阻塞延时

使用 join 语句, 让主线程等待子线程完成后才继续执行

子线程函数可以带参 threading.Timer(interval, function, args=[], kwargs={})

import threading

def threading_main():

print "main thread: start"

thrd = threading.Timer(5.0, threading_sub, args = ["sub thread"])

thrd.start()

print "main thread: wait"

thrd.join() # add this line

# thrd.join(timeout=2) # just wait 2s then continue

print "main thread: end"

def threading_sub(name):

print name + ": hello"

if __name__ == "__main__":

start = time.time()

threading_main()

end = time.time()

print "run time: {}".format(end - start)

装饰器

装饰器, 使用KThread,.localtrace结束线程. (通用性最好, 性能较低)

import threading

class Timeout(Exception):

"""function run timeout"""

class KThread(threading.Thread):

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

# Force the Thread to install our trace.

self.run = self.__run

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

def timeout(timeout, default=None, try_except=False):

"""Timeout decorator, parameter in timeout."""

def timeout_decorator(func):

def new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):

result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))

"""Wrap the original function."""

def func_wrapper(*args, **kwargs):

result = []

# create new args for _new_func, because we want to get the func

# return val to result list

new_kwargs = {

'oldfunc': func,

'result': result,

'oldfunc_args': args,

'oldfunc_kwargs': kwargs

}

thd = KThread(target=new_func, args=(), kwargs=new_kwargs)

thd.start()

thd.join(timeout)

# timeout or finished?

isAlive = thd.isAlive()

thd.kill()

if isAlive:

if try_except is True:

raise Timeout("{} Timeout: {} seconds.".format(func, timeout))

return default

else:

return result[0]

func_wrapper.__name__ = func.__name__

func_wrapper.__doc__ = func.__doc__

return func_wrapper

return timeout_decorator

if __name__ == "__main__":

import time

@timeout(5)

def count(name):

for i in range(10):

print("{}: {}".format(name, i))

time.sleep(1)

return "finished"

try:

print count("thread1")

print count("thread2")

except Timeout as e:

print e

将上面的例子, 改为函数调用模式, 这样timeout参数可灵活设置!

import threading

class Timeout(Exception):

"""function run timeout"""

class KThread(threading.Thread):

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

# Force the Thread to install our trace.

self.run = self.__run

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

def timeout_call(timeout, func, args=(), kwargs=None, default=None, try_except=False):

def new_func(oldfunc, result, oldfunc_args, oldfunc_kwargs):

result.append(oldfunc(*oldfunc_args, **oldfunc_kwargs))

result = []

kwargs = {} if kwargs is None else kwargs

# create new args for _new_func, because we want to get the func

# return val to result list

new_kwargs = {

'oldfunc': func,

'result': result,

'oldfunc_args': args,

'oldfunc_kwargs': kwargs

}

thd = KThread(target=new_func, args=(), kwargs=new_kwargs)

thd.start()

thd.join(timeout)

# timeout or finished?

isAlive = thd.isAlive()

thd.kill()

if isAlive:

if try_except is True:

raise Timeout("{} Timeout: {} seconds.".format(func, timeout))

return default

else:

return result[0]

if __name__ == "__main__":

import time

def count(name):

for i in range(10):

print("{}: {}".format(name, i))

time.sleep(1)

return "finished"

try:

print timeout_call(5, count, ["thread1"])

print timeout_call(5, count, ["thread2"])

except Timeout as e:

print e

装饰器, 使用thread.interrupt_main()结束线程. (仅可用于主线程)

import threading

def timeout_quit(fn_name):

thread.interrupt_main() # raises KeyboardInterrupt

def timeout(s):

'''

use as decorator to exit process if

function takes longer than s seconds

'''

def outer(fn):

def inner(*args, **kwargs):

timer = threading.Timer(s, timeout_quit, args=[fn.__name__])

timer.start()

try:

result = fn(*args, **kwargs)

finally:

timer.cancel()

return result

return inner

return outer

if __name__ == "__main__":

import time

@timeout(5)

def processNum(num):

time.sleep(2)

return num

try:

print processNum(1)

except KeyboardInterrupt:

print "timeout"

学习过程中的例子

threading.Timer + threading.join 多线程阻塞延时

使用 join 语句, 让主线程等待子线程完成后才继续执行

子线程函数可以带参 threading.Timer(interval, function, args=[], kwargs={})

import threading

def threading_main():

print "main thread: start"

thrd = threading.Timer(5.0, threading_sub, args = ["sub thread"])

thrd.start()

print "main thread: wait"

thrd.join() # add this line

# thrd.join(timeout=2) # just wait 2s then continue

print "main thread: end"

def threading_sub(name):

print name + ": hello"

if __name__ == "__main__":

start = time.time()

threading_main()

end = time.time()

print "run time: {}".format(end - start)

join(timeout=10) 多进程超时判断

multiprocessing的本质是进程, 但是提供了类似于threading的一系列方法.

使用 multiprocessing.terminate 语句, 让主线程可以杀死子线程

子进程函数可以带参 multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})

multiprocessing 没有 Timer() 方法的, 无法方便的延时执行.

注意, 这里没有办法使用 threading 类来实现. 因为没有 terminate() 方法,

而如果用signal方法来结束线程, 有两个限制. 1, windows不支持. 2, 子线程不支持

import multiprocessing

import logging

def processing_main():

print "main process: start"

prcs = multiprocessing.Process(

target=processing_sub, args=["sub process"])

prcs.start()

print "main process: wait"

prcs.join(timeout=10)

# If thread is still active

if prcs.is_alive():

print "main process: kill"

prcs.terminate()

prcs.join()

print "main process: end"

def processing_sub(name):

for i in range(100):

# if use print, can not show immediately in the console.

logging.error("{}: {}".format(name, i))

time.sleep(1)

if __name__ == "__main__":

start = time.time()

processing_main()

end = time.time()

print "run time: {}".format(end - start)

multiprocessing.pool 实现超时判断

说说python下的 thread 和 process.

thread, 提供了signal结束方式, 但是windows不支持, 仅主线程可用! 换句话说, 终止线程很繁琐

process, 提供了terminate结束方式, 但是参数传递限制条件很多, (必须可以是pickle的…)

下面的代码是有问题的!!!

import functools

def timeout(timeout, default=None, try_except=False):

"""Timeout decorator, parameter in seconds."""

def timeout_decorator(item):

"""Wrap the original function."""

@functools.wraps(item)

def func_wrapper(*args, **kwargs):

"""Closure for function."""

pool = multiprocessing.pool.ThreadPool(processes=1)

# pool = multiprocessing.pool.Pool(processes=1) ## raise error about pickle problem!!!

try:

async_result = pool.apply_async(item, args, kwargs)

val = async_result.get(timeout)

except multiprocessing.TimeoutError:

pool.terminate() ## not work here, because it is acutally thread, not process!!!

val = default

if try_except is True:

raise multiprocessing.TimeoutError

else:

pool.close()

pool.join()

return val

return func_wrapper

return timeout_decorator

if __name__ == "__main__":

import time

@timeout(5)

def count(name):

for i in range(10):

print("{}: {}".format(name, i))

time.sleep(1)

return "finished"

start = time.time()

print count("thread1")

print count("thread2") ## you can find problem here, thread1 is still running...

end = time.time()

print "run time: {}".format(end - start)

def timeout_call(timeout, func, args=(), kwargs=None, default=None, try_except=False):

kwargs = {} if kwargs is None else kwargs

pool = multiprocessing.Pool(processes=1)

try:

async_result = pool.apply_async(func, args, kwargs)

val = async_result.get(timeout)

except multiprocessing.TimeoutError:

pool.terminate()

val = default

if try_except is True:

raise multiprocessing.TimeoutError

else:

pool.close()

pool.join()

return val

################### example ##########

import logging

import time

def count(name):

for i in range(10):

logging.error("{}: {}".format(name, i))

time.sleep(1)

return "finished"

if __name__ == "__main__":

## if count function is here, will raise error!!!

start = time.time()

print timeout_call(5, count, ["process1"])

print timeout_call(5, count, ["process2"])

end = time.time()

print "run time: {}".format(end - start)

第三方方案

timeoutcontext 1.1.1

基于signal实现, 不支持windows系统, 不支持子线程

timeout-decorator 0.3.2

signal或Multithreading可选

使用signal时, 不支持windows, 不支持子线程

使用Multithreading时, 无法返回不能pickle的数据(因为需要通过pickle来跨进程交换数据)

stopit 1.1.1

threading或signal可选

计时误差太大, 不可接受(翻倍的误差)

使用gevent协程

from gevent import Timeout

time_to_wait = 5 # seconds

class TooLong(Exception):

pass

with Timeout(time_to_wait, TooLong):

gevent.sleep(10)

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值