I'm calling a function in Python which I know may stall and force me to restart the script. How do I call the function or what do I wrap it in so that if it takes longer than 5 seconds the script cancels it and does something else.
Thanks
分类: LINUX
2014-06-06 16:12:46
原文地址:python 在规定时间内执行程序 作者:mrpre
点击(此处)折叠或打开
29
22
|
I'm calling a function in Python which I know may stall and force me to restart the script. How do I call the function or what do I wrap it in so that if it takes longer than 5 seconds the script cancels it and does something else. Thanks |
||||||||||||
|
17
|
I'm making some local xmlrpc calls with a timeout using the following code, borrowed from an ActiveState Cookbook recipe: def timeout(func, args=(), kwargs={}, timeout_duration=10, default=None): """This function will spawn a thread and run the given function using the args, kwargs and return the given default value if the timeout_duration is exceeded. """ import threading class InterruptableThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.result = default def run(self): self.result = func(*args, **kwargs) it = InterruptableThread() it.start() it.join(timeout_duration) if it.isAlive(): return it.result else: return it.result Invoking it with a 5 second timeout: result = timeout(remote_calculate, (myarg,), timeout_duration=5) |
||||||||||||||||||||
|
44
|
You may use the signal package if you are running on UNIX: In [1]: import signal # Register an handler for the timeout In [2]: def handler(signum, frame): ...: print "Forever is over!" ...: raise Exception("end of time") ...: # This function *may* run for an indetermined time... In [3]: def loop_forever(): ...: import time ...: while 1: ...: print "sec" ...: time.sleep(1) ...: ...: # Register the signal function handler In [4]: signal.signal(signal.SIGALRM, handler) Out[4]: 0 # Define a timeout for your function In [5]: signal.alarm(10) Out[5]: 0 In [6]: try: ...: loop_forever() ...: except Exception, exc: ...: print exc ....: sec sec sec sec sec sec sec sec Forever is over! end of time # Cancel the timer if the function returned before timeout # (ok, mine won't but yours maybe will :) In [7]: signal.alarm(0) Out[7]: 0 10 seconds after the call alarm.alarm(10), the handler is called. This raises an exception that you can intercept from the regular Python code. This module doesn't play well with threads (but then, who does?) |
||||||||||||||||||||
|
7
|
You can use multiprocessing.Process to do exactly that. Code import multiprocessing import time # bar def bar(): for i in range(100): print "Tick" time.sleep(1) if __name__ == '__main__': # Start bar as a process p = multiprocessing.Process(target=bar) p.start() # Wait for 10 seconds or until process finishes p.join(10) # If thread is still active if p.is_alive(): print "running... let's kill it..." # Terminate p.terminate() p.join() |
||||||||||||
|
3
|
If this is some kind of network or file operation, you might also consider using nonblocking IO. This can be a better option if you're doing a lot of these types of operations at once (otherwise, you can bog your system down fairly quickly with a lot of threads). Here's a socket howto that covers nonblocking IO (in the context of network operations). The downside? Well, it can be a pain to program. Sometimes even moreso than just using a thread. |
||
add comment |
2
|
Maybe try to call it from other thread, which You could easily terminate. |
||||||||
|
2
|
What yabcok said - start a new thread to call the function. In the original thread, sleep for 5 seconds, then terminate the function thread if it hasn't already ended. Maybe there is a better approach to your problem? Why might the function take longer than 5 seconds? |
||
add comment |
2
|
I have a different proposal which is a pure function (with the same API as the threading suggestion) and seems to work fine (based on suggestions on this thread) def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None): import signal class TimeoutError(Exception): pass def handler(signum, frame): raise TimeoutError() # set the timeout handler signal.signal(signal.SIGALRM, handler) signal.alarm(timeout_duration) try: result = func(*args, **kwargs) except TimeoutError as exc: result = default finally: signal.alarm(0) return result |
||||||||||||
|
0
|
Here is a slight improvement to the given thread-based solution. The code below supports exceptions: def runFunctionCatchExceptions(func, *args, **kwargs): try: result = func(*args, **kwargs) except Exception, message: return ["exception", message] return ["RESULT", result] def runFunctionWithTimeout(func, args=(), kwargs={}, timeout_duration=10, default=None): import threading class InterruptableThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.result = default def run(self): self.result = runFunctionCatchExceptions(func, *args, **kwargs) it = InterruptableThread() it.start() it.join(timeout_duration) if it.isAlive(): return default if it.result[0] == "exception": raise it.result[1] return it.result[1] Invoking it with a 5 second timeout: result = timeout(remote_calculate, (myarg,), timeout_duration=5) |
||||
|
0
|
Jeff version is great that I am using it in a production. However, I have noticed that exception raised inside the function (now an independent thread) are not communicated back to the caller. So here is my workaround it. import sys import threading from datetime import datetime def timed_run(func, args=(), kwargs={}, timeout=10, default=None): """This function will spawn a thread and run the given function using the args, kwargs and return the given default value if the timeout is exceeded. """ class InterruptableThread(threading.Thread): def __init__(self): threading.Thread.__init__(self) self.result = default self.exc_info = (None, None, None) def run(self): try: self.result = func(*args, **kwargs) except Exception as e: self.exc_info = sys.exc_info() def suicide(self): raise RuntimeError('Stop has been called') it = InterruptableThread() it.start() print("calling %(func)r for %(timeout)r seconds" % locals()) started_at = datetime.now() it.join(timeout) ended_at = datetime.now() diff = ended_at - started_at print("%(f)s exited after %(d)r seconds" % {'f': func, 'd': diff.seconds}) if it.exc_info[0] is not None: # if there were any exceptions a,b,c = it.exc_info raise a,b,c # communicate that to caller if it.isAlive(): it.suicide() raise RuntimeError("%(f)s timed out after %(d)r seconds" % {'f': func, 'd': diff.seconds}) else: return it.result This will raise the exception providing a full traceback from the line inside the thread that originated the error. |
||||
|
0
|
We can use signals for the same. I think the below example will be useful for you. It is very simple compared to threads. import signal def timeout(signum, frame): raise myException #this is an infinite loop, never ending under normal circumstances def main(): print 'Starting Main ', while 1: print 'in main ', #SIGALRM is only usable on a unix platform signal.signal(signal.SIGALRM, timeout) #change 5 to however many seconds you need signal.alarm(5) try: main() except myException: print "whoops" |
||||||||
|
Name
Email
|
By posting your answer, you agree to the privacy policy and terms of service.