在timeit内调用Python函数超时

时间:2013-12-03 20:16:00

标签: python python-2.7 timeout python-multithreading timeit

我有一个函数,我们称之为my_function(*args, **kwargs),根据传递给它的参数,它需要30秒到很多小时(天)。我有一个不同参数的列表,我想知道函数在给定参数的情况下需要多长时间。

我仍然很擅长使用timeit,但我已经学到了足够的知识来完成这一部分;但是,这对我的目的来说不是很有效。尽管所有这些参数都可以在“有限”时间内解决,但任何需要超过4小时才能完成的参数都会被认为是难以处理的。由于一些(可能是大多数)参数将导致运行时间超过20小时,我正在寻找一种方法来在4小时后终止测试,这样我就不必浪费时间来搞清楚这是棘手的。

我查看了Timeout on a Python function callStop code after time period,这可能是一个重复的问题,但我无法将这些答案与timeit整合在一起记录的时间少于4小时,长时间运行会返回大于4小时的有效时间。

这样做最好的方法是什么?

编辑:我遇到的一个问题是,我看到的答案在func(*args,**kwargs) timeit,而timeit.Timer('my_function(*args, **kwargs)', setup=setup).timeit(1) 函数看起来像这样:

def foo(x):
    for i in xrange(1, x + 1):
        sleep(1)
        print i
    return x

我不知道如何处理这张表格。

编辑:我在下面使用线程提供的原始答案实际上并不终止线程。通过以下功能运行它可以很容易地显示出来。

multiprocessing.Pool

使用涉及terminate()的代码,实际上有{{1}},允许这样做。

2 个答案:

答案 0 :(得分:1)

基于Timeout function using threading in python does not work中的答案。如果你在foo(x)上试一试,它确实会停止计数,这与我以前的答案不同。

import multiprocessing as mp
import timeit

def timeout(func, args=(), kwargs=None, TIMEOUT=10, default=None, err=.05):

    if hasattr(args, "__iter__") and not isinstance(args, basestring):
        args = args
    else:
        args = [args]    
    kwargs = {} if kwargs is None else kwargs

    pool = mp.Pool(processes = 1)

    try:
        result = pool.apply_async(func, args=args, kwds=kwargs)
        val = result.get(timeout = TIMEOUT * (1 + err))
    except mp.TimeoutError:
        pool.terminate()
        return default
    else:
        pool.close()
        pool.join()
        return val

def Timeit(command, setup=''):
    return timeit.Timer(command, setup=setup).timeit(1)

def timeit_timeout(command, setup='', TIMEOUT=10, default=None, err=.05):
    return timeout(Timeit, args=command, kwargs={'setup':setup},
                   TIMEOUT=TIMEOUT, default=default, err=err) 

答案 1 :(得分:0)

经过一些小小的讨论后,我得到了基于Timeout function using threading的初步答案。我仍然希望听到任何有更好想法的人,因为我还是新手。

def timeout(func, args=None, kwargs=None, TIMEOUT=10, default=None, err=.05):
    if args is None:
        args = []
    elif hasattr(args, "__iter__") and not isinstance(args, basestring):
        args = args
    else:
        args = [args]

    kwargs = {} if kwargs is None else kwargs

    import threading
    class InterruptableThread(threading.Thread):
        def __init__(self):
            threading.Thread.__init__(self)
            self.result = None

        def run(self):
            try:
                self.result = func(*args, **kwargs)
            except:
                self.result = default

    it = InterruptableThread()
    it.start()
    it.join(TIMEOUT* (1 + err))
    if it.isAlive():
        return default
    else:
        return it.result

def timeit_timeout(command, setup='', TIMEOUT=10, default=None, err=.05):
    import timeit
    f = lambda: timeit.Timer(command, setup=setup).timeit(1)
    return timeout(f,TIMEOUT=TIMEOUT, default=default, err=err) 

if __name__ == '__main__':    
    from time import sleep
    setup = 'gc.enable()\nfrom time import sleep'
    for n in range(1,15):
        command = 'sleep({})'.format(n)
        print timeit_timeout(command, setup=setup, default=float('Inf'))