right way to run some code with timeout in Python
我在网上找到了一些关于超时运行代码的讨论和活动模式。看起来有一些常见的方法:
- 使用运行代码的线程,并使用带有超时的
join 。如果超时时间已过-终止线程。这在python(使用的私有_Thread__stop 函数)中不直接支持,因此这是不好的做法。 - 使用
signal.SIGALRM —但这种方法不适用于Windows! - 使用带有超时的子进程-但这太重了-如果我想经常启动可中断的任务,我不希望为每个任务都触发进程怎么办!
那么,正确的方法是什么?我不是在问解决方法(例如使用Twisted和AsyncIO),而是实际解决实际问题的方法——我有一些函数,我只想在超时的情况下运行它。如果超时时间已过,我希望控制权恢复。我希望它能在Linux和Windows上工作。
一个完全通用的解决方案,真的,真的不存在。对于给定的域,必须使用正确的解决方案。
如果你想让你完全控制的代码超时,你必须写它来合作。这种代码必须能够以某种方式分解成小块,比如在事件驱动系统中。如果您可以确保没有什么东西能将锁保持太长时间,那么您也可以通过线程来实现这一点,但正确处理锁实际上相当困难。
如果您因为担心代码失控而需要超时(例如,如果您担心用户会要求计算器计算
9**(9**9) ),则需要在另一个进程中运行它。这是唯一能够充分隔离它的简单方法。在事件系统或其他线程中运行它是不够的。也可以将事情分成与其他解决方案类似的小块,但需要非常小心的处理,通常不值得这样做;无论如何,这不允许您执行与运行Python代码相同的精确操作。
您可能正在寻找的是多处理模块。如果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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
1 2 3 4 5 6 7 | from eventlet.timeout import Timeout timeout = Timeout(seconds, exception) try: ... # execution here is limited by timeout finally: timeout.cancel() |
如果与网络相关,您可以尝试:
1 2 | import socket socket.setdefaulttimeout(number) |
另一种方法是使用FaultHandler:
1 2 3 4 5 6 7 8 9 10 11 12 | 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的一部分。
对于不在C扩展或I/O等待中延迟延长时间的"普通"python代码,可以通过使用
这是否足够取决于您运行的代码是如何协作还是恶意的。如果它的行为良好,跟踪函数就足够了。
用"with"构造求解并合并来自-
- 超时函数(如果完成时间过长)
这条线更管用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36import 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
我就是这样解决的:因为我工作得很好(在窗户里,一点也不重),我希望它对别人有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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) |
主要的想法是创建一个线程,该线程将与"long work"并行睡眠,并在唤醒(超时后)时更改安全变量状态,long函数在工作期间检查安全变量。我对python编程很陌生,所以如果这个解决方案有一个基本错误,比如资源、时间、死锁问题,请回答)。
如果您运行的代码希望在一个设定的时间之后死亡,那么您应该正确地编写它,这样无论它是线程还是子进程,都不会对关闭产生任何负面影响。这里有一个带撤销的命令模式是很有用的。
所以,这真的取决于你杀死它时线程在做什么。如果只是计算数字谁在乎你杀了它。如果它与文件系统交互,而您杀死了它,那么您可能真的应该重新考虑您的策略。
关于线程,python支持什么?守护进程线程和联接。如果您加入了一个仍处于活动状态的守护进程,为什么Python会让主线程退出?因为它知道使用守护进程线程的人(希望)会以线程死亡时不重要的方式编写代码。在这个上下文中,给一个连接超时,然后让主节点死掉,这样就可以使用任何守护进程线程。