在Python中使用超时运行一些代码的正确方法

时间:2011-08-04 18:59:19

标签: python windows multithreading timeout

我在线查看了一些SO讨论和ActiveState配方,用于运行一些超时的代码。看起来有一些常见的方法:

  • 使用运行代码的线程,并使用超时join。如果超时已超时 - 终止该线程。这不是Python直接支持的(使用私有_Thread__stop函数)因此不良做法
  • 使用signal.SIGALRM - 但此方法无法在Windows上运行
  • 使用带有超时的子进程 - 但这太重 - 如果我想经常启动可中断任务,我不希望每个进程都有火![/ li>

那么,正确的方式是什么?我不是在询问有关变通方法(例如使用Twisted和async IO),而是解决实际问题的实际方法 - 我有一些功能,我想只运行一些超时。如果超时,我想要控制回来。我希望它能在Linux和Windows上运行。

9 个答案:

答案 0 :(得分:9)

您可能正在寻找的是multiprocessing模块。如果subprocess太重,那么这可能也不适合您的需求。

import time
import multiprocessing

def do_this_other_thing_that_may_take_too_long(duration):
    time.sleep(duration)
    return 'done after sleeping {0} seconds.'.format(duration)

pool = multiprocessing.Pool(1)
print 'starting....'
res = pool.apply_async(do_this_other_thing_that_may_take_too_long, [8])
for timeout in range(1, 10):
    try:
        print '{0}: {1}'.format(duration, res.get(timeout))
    except multiprocessing.TimeoutError:
        print '{0}: timed out'.format(duration) 

print 'end'

答案 1 :(得分:9)

这个的完全通用解决方案真的,老实说不存在。您必须为给定的域使用正确的解决方案。

  • 如果您希望完全控制代码超时,则必须将其编写为合作。这样的代码必须能够以某种方式分解成小块,就像在事件驱动的系统中一样。你也可以通过线程来做到这一点,如果你可以确保没有任何东西会锁定太长时间,但是处理锁定实际上非常困难。

  • 如果你想要超时,因为你害怕代码失控(例如,如果你担心用户会要求你的计算器计算9**(9**9)),你需要运行它另一个过程。这是充分隔离它的唯一简单方法。在事件系统或甚至不同的线程中运行它是不够的。也可以将其分解成与其他解决方案类似的小块,但需要非常小心的处理,通常是不值得的;无论如何,这不允许你做与运行Python代码完全相同的事情。

答案 2 :(得分:4)

我在eventlet库中找到了这个:

http://eventlet.net/doc/modules/timeout.html

from eventlet.timeout import Timeout

timeout = Timeout(seconds, exception)
try:
    ... # execution here is limited by timeout
finally:
    timeout.cancel()

答案 3 :(得分:3)

如果与网络相关,您可以尝试:

import socket
socket.setdefaulttimeout(number)

答案 4 :(得分:2)

对于“普通”Python代码,在C扩展或I / O等待中不会延长时间,您可以通过设置跟踪函数sys.settrace()来实现目标,该函数在超时时中止正在运行的代码到了。

这是否足够取决于您运行的代码的合作或恶意程度。如果它表现良好,则跟踪功能就足够了。

答案 5 :(得分:2)

另一种方法是使用faulthandler

import time
import faulthandler


faulthandler.enable()


try:
    faulthandler.dump_tracebacks_later(3)
    time.sleep(10)
finally:
    faulthandler.cancel_dump_tracebacks_later()

N.B:faulthandler模块是python3.3中stdlib的一部分。

答案 6 :(得分:0)

如果您正在运行预期在设定时间后死亡的代码,那么您应该正确编写它,以便对关闭没有任何负面影响,无论是线程还是子进程。带撤消的命令模式在这里很有用。

所以,它实际上取决于你杀死它时线程正在做什么。如果它只是处理数字谁关心你是否杀了它。如果它与文件系统进行交互并且你杀了它,那么也许你应该重新考虑你的策略。

在线程方面,Python支持什么?守护程序线程和连接。为什么如果你在守护进程仍处于活动状态时加入一个守护进程,python会让主线程退出?因为它理解有人使用守护程序线程(希望)以一种在该线程死亡时无关紧要的方式编写代码。在这种情况下,为连接提供超时,然后让主要死亡,从而使用任何守护程序线程,是完全可以接受的。

答案 7 :(得分:0)

我以这种方式解决了这个问题: 对我来说工作得很好(在Windows中并且根本不重)我希望它对某人有用)

import threading
import time

class LongFunctionInside(object):
    lock_state = threading.Lock()
    working = False

    def long_function(self, timeout):

        self.working = True

        timeout_work = threading.Thread(name="thread_name", target=self.work_time, args=(timeout,))
        timeout_work.setDaemon(True)
        timeout_work.start()

        while True:  # endless/long work
            time.sleep(0.1)  # in this rate the CPU is almost not used
            if not self.working:  # if state is working == true still working
                break
        self.set_state(True)

    def work_time(self, sleep_time):  # thread function that just sleeping specified time,
    # in wake up it asking if function still working if it does set the secured variable work to false
        time.sleep(sleep_time)
        if self.working:
            self.set_state(False)

    def set_state(self, state):  # secured state change
        while True:
            self.lock_state.acquire()
            try:
                self.working = state
                break
            finally:
                self.lock_state.release()

lw = LongFunctionInside()
lw.long_function(10)

主要的想法是创建一个线程,它将与“长时间工作”平行休眠。并且在唤醒(超时后)更改安全变量状态时,长函数在其工作期间检查安全变量。 我是Python编程的新手,所以如果该解决方案有基本错误,如资源,时间,死锁问题,请回复))。

答案 8 :(得分:0)

使用'解决问题。构建和合并来自

的解决方案
  • Timeout function if it takes too long to finish
  • 这个线程更好用。

    import threading, time
    
    class Exception_TIMEOUT(Exception):
        pass
    
    class linwintimeout:
    
        def __init__(self, f, seconds=1.0, error_message='Timeout'):
            self.seconds = seconds
            self.thread = threading.Thread(target=f)
            self.thread.daemon = True
            self.error_message = error_message
    
        def handle_timeout(self):
            raise Exception_TIMEOUT(self.error_message)
    
        def __enter__(self):
            try:
                self.thread.start()
                self.thread.join(self.seconds)
            except Exception, te:
                raise te
    
        def __exit__(self, type, value, traceback):
            if self.thread.is_alive():
                return self.handle_timeout()
    
    def function():
        while True:
            print "keep printing ...", time.sleep(1)
    
    try:
        with linwintimeout(function, seconds=5.0, error_message='exceeded timeout of %s seconds' % 5.0):
            pass
    except Exception_TIMEOUT, e:
        print "  attention !! execeeded timeout, giving up ... %s " % e