Chinaunix首页 | 论坛 | 博客
  • 博客访问: 194804
  • 博文数量: 30
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 476
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-07 18:15
个人简介

程序员一个。14年毕业。

文章分类

全部博文(30)

文章存档

2014年(13)

2013年(17)

我的朋友

分类: LINUX

2013-12-29 20:23:28

不知道python有没有定时器之类的东西,之前拿python写一个脚本,里面调用shell命令,3000多条命令,不知道为什么,执行少则3条,多则300条,后,就会卡子那边不动了,很无语。百度了一个函数,非常好使。帖子一共7种方法,非常不错,贴出我选择的一个方法如下:

点击(此处)折叠或打开

  1. from time import sleep

  2. def timeout(func, args=(), kwargs={}, timeout_duration=10, default="fail"):
  3.     """This function will spawn a thread and run the given function
  4.     using the args, kwargs and return the given default value if the
  5.     timeout_duration is exceeded.
  6.     """
  7.     import threading
  8.     class InterruptableThread(threading.Thread):
  9.         def __init__(self):
  10.             threading.Thread.__init__(self)
  11.             self.result = default
  12.         def run(self):
  13.             self.result = func(*args, **kwargs)
  14.     it = InterruptableThread()
  15.     it.start()
  16.     it.join(timeout_duration)
  17.     if it.isAlive():
  18.         return it.result
  19.     else:
  20.         return it.result


  21. def remote_calculate(st):
  22.     print st
  23.     sleep(3)
  24.     print st
  25.     sleep(5)
  26.     print "end"
  27.     return "sucess"

 #用法
  1. result = timeout(remote_calculate, ("in",), timeout_duration=5)
  2. print result
remote_calculate函数自己设置睡眠时间,就可以看到这个超时函数是否起效了,非常管用。
这是被选择的答案,也就是1楼的答案,虽然答案的作者自己也承认了,这是一个错误的答案,但是我的程序不需要考虑这个问题,所以还是使用了这个“错误的”答案。

原文地址:万一链接失效了呢!
Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

Work at Applifier Oy

  • Helsinki, Finland
Flexible working hours
Top-notch laptops for all!
Catered breakfast on Fridays

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

share|improve this question
 
Does "stall" mean "run indefinitely" or just "run a few seconds longer than I want to wait, but it will always terminate properly"? It makes a big difference on what the proper answer is for your question. –  Brandon Jan 29 '09 at 20:11
 
Hang for at least 20 seconds though the time period should logically be variable to the situation being dealt with. –  Teifion Jan 30 '09 at 12:02
 
I think you need to distinguish between 2 cases: (simple) - a timeout on pure python function-calls, and (annoying), implementing a timeout on external calls. I suspect that what might work best for one will not be best for the other. –  Salim Fadhley Feb 2 '09 at 13:32
add comment

10 Answers

up vote17down voteaccepted

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)
share|improve this answer
12  
Doesn't Thread.join() just block until the timeout? If the thread doesn't terminate normally, it will continue to run in the background consuming CPU, perhaps for the lifetime of the app? –  Brandon Jan 29 '09 at 20:09
7  
is something wrong with last 4 lines? return result regardless of thread state? why check it then? should it be raise in the first branch? –  Fluffy Oct 7 '09 at 11:58
 
Agree with Brandon Corfman here. –  xitrium Aug 2 '10 at 8:35
3  
I'd suggest that the if/else block should have "return default" in the then-branch. That argument is currently completely unused. Also beware that the class InterruptableThread contradicts its name: It does not implement interruption of the thread. Also I fail to see why an extra class is needed here (it is only used as result storage). Note also that exceptions will escape the new thread (as in: they will be passed to sys.excepthook and otherwise ignored). –  Bluehorn Dec 19 '11 at 12:28 
 
@Brandon is correct, Thread.join() blocks until the timeout. I would delete my response, but I can't delete an accepted answer. Please see piro's code below for a solution using signals (not available on Windows platforms). –  Jeff Bauer Aug 28 '12 at 20:32
show 1 more comment

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?)

share|improve this answer
2  
VHDL plays pretty well with the idea of threading because everything is concurrent ;) –  Teifion Jan 30 '09 at 12:03
 
Great solution. The advantage of this approach is that it can interrupt almost anything. The disadvantage is it requires python 2.5 or newer... all you luddites better use an alternative method. –  Salim Fadhley Feb 2 '09 at 13:26
1  
I use Python 2.5.4. There is such an error: Traceback (most recent call last): File "aa.py", line 85, in func signal.signal(signal.SIGALRM, handler) AttributeError: 'module' object has no attribute 'SIGALRM' –  flypenMay 13 '11 at 1:59 
1  
@flypen that's because signal.alarm and the related SIGALRM are not available on Windows platforms.–  Double AA Aug 19 '11 at 16:20
1  
If there are a lot of processes, and each calls signal.signal --- will they all work properly? Won't eachsignal.signal call cancel "concurrent" one? –  brownian May 10 '12 at 8:28
show 4 more comments

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()
share|improve this answer
 
p.is_alive() always seems to return true? –  Steve Bennett May 21 at 2:48
 
Just prints "running... let's kill it..." after 10 seconds running Python 2.7.5 on Windows 8. –  Wallacoloo Jul 4 at 1:25
 
@ATOzTOA forgot to add the parenthesis. It should be p.is_alive() –  tepedizzle Jul 9 at 21:09 
add comment

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.

share|improve this answer
add comment

Maybe try to call it from other thread, which You could easily terminate.

share|improve this answer
1  
There is no method in the thread API for terminating a thread. The function must terminate normally for the thread to end, unless you want to resort to platform-specific hacks. –  Brandon Jan 29 '09 at 20:33
 
oops :) that's a pitty –  Jacek ?awrynowicz Jan 30 '09 at 13:30
add comment

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?

share|improve this answer
add comment

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
share|improve this answer
 
This is the best solution here. –  Martin Konecny Jun 11 at 15:08
 
You should also restore the original signal handler. See stackoverflow.com/questions/492519/… – Martin Konecny Jun 11 at 15:21
1  
One more note: The Unix signal method only works if you are applying it in the main thread. Applying it in a sub-thread throws an exception and will not work. –  Martin Konecny Jun 12 at 20:23
add comment

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)
share|improve this answer
 
This will raise a new exception hiding the original traceback. See my version below... –  Meitham Dec 14 '12 at 11:20
add comment

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.

share|improve this answer
 
no. this does not kill the thread. I just tried it. An exception is raised inside the method suicide(), but it does not kill the container thread –  Moataz Elmasry Apr 27 at 22:41 
add comment

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"
share|improve this answer
 
It would be better to choose a specific exception and to catch only it. Bare try: ... except: ... are always a bad idea. –  hivert Jul 23 at 11:28
 
I agree with you hivert. –  user2599593 Jul 26 at 6:58
add comment

Your Answer

 

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged    or ask your own question.


阅读(5567) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~