正确的方式来在Python中运行一些超时的代码

我看了网上,发现一些讨论和ActiveState食谱运行一些超时的代码。 看起来有一些常见的方法:

  • 使用运行代码的线程,并使用超时join 。 如果超时 – 杀死线程。 这不是Python直接支持的(使用private _Thread__stop函数),所以这是不好的做法
  • 使用signal.SIGALRM – 但这种方法不适用于Windows
  • 使用超时的subprocess – 但是这太重了 – 如果我想要经常开始可中断的任务,我不想为每个进程启动进程!

那么,什么是正确的方法 ? 我不是问变通方法(例如使用扭曲和asynchronousIO),但实际的方式来解决实际问题 – 我有一些function,我想只运行一些超时。 如果超时,我想控制回来。 我希望它能在Linux和Windows上运行。

对此一个完全一般的解决方案确实不存在。 您必须为给定的域使用正确的解决方案。

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

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

你可能要找的是多处理模块。 如果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' 

我用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() 

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

这是否足够取决于你运行的代码是如何协作或恶意的。 如果表现良好,跟踪功能就足够了。

另一种方法是使用错误处理程序 :

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

注意: faulthandler模块是python3.3中的stdlib的一部分。

如果是网络相关的,你可以尝试:

 import socket socket.setdefaulttimeout(number) 

如果你正在运行一段时间之后的代码,那么你应该正确地编写代码,以便对关闭没有任何负面影响,不管它是一个线程还是一个子进程。 一个命令模式与撤消在这里是有用的。

所以,这真的取决于线程在杀死它时做了什么。 如果只是扼杀的数字谁在乎你是否杀了它。 如果它与文件系统相互作用,并且杀死它,那么也许你应该重新考虑你的策略。

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

我以这种方式解决了这个问题:对我来说,工作很棒(在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编程,所以如果解决方案有一个基本的错误,如资源,时间,死锁问题,请回应))。

解决与'与'构建和合并解决方案 –

  • 超时功能,如果它需要太长时间才能完成
  • 这个线程哪个更好用。

     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