함수 호출시 시간 초과


300

파이썬에서 멈추고 스크립트를 다시 시작하라는 함수를 파이썬에서 호출하고 있습니다.

함수를 어떻게 호출하거나 5 초 이상 걸리면 스크립트가 취소하고 다른 작업을 수행하도록 랩핑하는 방법은 무엇입니까?

답변:


227

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 초 alarm.alarm(10) 핸들러가 호출됩니다. 일반 파이썬 코드에서 가로 챌 수있는 예외가 발생합니다.

이 모듈은 스레드와 잘 작동하지 않습니다 (하지만 누가 사용합니까?)

하는 것으로 타임 아웃이 발생하면 우리는 예외를 발생하기 때문에, 그것은 하나 개의 기능, 예를 들어, 적발 및 함수 내에서 무시 끝낼 수 있습니다 :

def loop_forever():
    while 1:
        print('sec')
        try:
            time.sleep(10)
        except:
            continue

5
파이썬 2.5.4를 사용합니다. 이러한 오류가 있습니다. Traceback (가장 최근 호출) : func signal.signal (signal.SIGALRM, handler)의 85 번째 줄 "aa.py"파일 AttributeError : 'module'개체에 'SIGALRM'속성이 없습니다.
flypen

11
@flypen 때문이며 Windows 플랫폼 signal.alarm에서는 관련 기능 SIGALRM을 사용할 수 없습니다.
더블 AA

2
프로세스가 많고 각 호출이 signal.signal--- 모두 제대로 작동합니까? 각 signal.signal호출이 "동시"호출을 취소 하지 않습니까?
brownian

1
이것을 C 확장과 함께 사용하려는 경우 경고 : C 함수가 제어를 Python 인터프리터로 리턴 할 때까지 Python 신호 핸들러가 호출되지 않습니다. 이 사용 사례에 대해서는 ATOzTOA의 답변을 사용하십시오. stackoverflow.com/a/14924210/1286628
wkschwartz

13
스레드에 대한 경고를 두 번째로합니다. signal.alarm은 메인 스레드에서만 작동합니다. Django 뷰에서 이것을 사용하려고했습니다. 메인 스레드에 대한 언어로 즉시 실패합니다.
JL Peyret

154

당신이 사용할 수있는 multiprocessing.Process정확히 그렇게 .

암호

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

36
대상 메서드의 반환 값을 어떻게 얻을 수 있습니까?
bad_keypoints

4
호출 된 함수가 I / O 블록에 멈춰 있으면 작동하지 않는 것 같습니다.
sudo

4
@bad_keypoints이 답변 참조 : stackoverflow.com/a/10415215/1384471 기본적으로 답변을 입력 한 목록을 전달합니다.
Peter

1
@sudo를 제거하십시오 join(). 그것은 x 개의 동시 서브 프로세스가 작업을 완료 할 때까지 또는 실행중인 양을 정의 할 때까지 실행됩니다 join(10). 10 개의 프로세스에 대해 차단 I / O가있는 경우 join (10)을 사용하여 시작된 각 프로세스에 대해 최대 10까지 대기하도록 설정했습니다. 이 예제와 같은 데몬 플래그를 사용하십시오 . stackoverflow.com/a/27420072/2480481 물론 u는 플래그 daemon=Truemultiprocessing.Process()함수에 직접 전달할 수 있습니다 .
m3nda

2
@ATOzTOA 적어도이 목적을 위해이 솔루션의 문제점은 잠재적으로 어린이 발자국이 스스로 청소할 수 없다는 것입니다. 종료 기능 문서terminate() ... Note that exit handlers and finally clauses, etc., will not be executed. Note that descendant processes of the process will not be terminated – they will simply become orphaned.
abalcerek

78

함수를 어떻게 호출하거나 5 초 이상 걸리면 스크립트가 취소하도록 랩핑하는 방법은 무엇입니까?

나는 이 질문 / 문제를 해결 하는 요점 을 데코레이터와threading.Timer . 여기에 고장이 있습니다.

호환성을위한 가져 오기 및 설정

Python 2 및 3으로 테스트되었으며 Unix / Linux 및 Windows에서도 작동합니다.

먼저 수입품. 다음은 Python 버전에 관계없이 코드 일관성을 유지하려는 시도입니다.

from __future__ import print_function
import sys
import threading
from time import sleep
try:
    import thread
except ImportError:
    import _thread as thread

버전 독립적 코드 사용 :

try:
    range, _print = xrange, print
    def print(*args, **kwargs): 
        flush = kwargs.pop('flush', False)
        _print(*args, **kwargs)
        if flush:
            kwargs.get('file', sys.stdout).flush()            
except NameError:
    pass

이제 표준 라이브러리에서 기능을 가져 왔습니다.

exit_after 데코레이터

다음으로 main()자식 스레드에서 종료하는 함수가 필요합니다 .

def quit_function(fn_name):
    # print to stderr, unbuffered in Python 2.
    print('{0} took too long'.format(fn_name), file=sys.stderr)
    sys.stderr.flush() # Python 3 stderr is likely buffered.
    thread.interrupt_main() # raises KeyboardInterrupt

그리고 여기 데코레이터 자체가 있습니다.

def exit_after(s):
    '''
    use as decorator to exit process if 
    function takes longer than s seconds
    '''
    def outer(fn):
        def inner(*args, **kwargs):
            timer = threading.Timer(s, quit_function, args=[fn.__name__])
            timer.start()
            try:
                result = fn(*args, **kwargs)
            finally:
                timer.cancel()
            return result
        return inner
    return outer

용법

다음은 5 초 후에 종료에 대한 귀하의 질문에 직접 답변하는 사용법입니다!

@exit_after(5)
def countdown(n):
    print('countdown started', flush=True)
    for i in range(n, -1, -1):
        print(i, end=', ', flush=True)
        sleep(1)
    print('countdown finished')

데모:

>>> countdown(3)
countdown started
3, 2, 1, 0, countdown finished
>>> countdown(10)
countdown started
10, 9, 8, 7, 6, countdown took too long
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner
  File "<stdin>", line 6, in countdown
KeyboardInterrupt

두 번째 함수 호출이 완료되지 않고 대신 프로세스가 역 추적로 종료됩니다!

KeyboardInterrupt 잠자는 실을 항상 멈추지는 않습니다

Windows의 Python 2에서 키보드 인터럽트로 인해 절전 모드가 항상 중단되는 것은 아닙니다.

@exit_after(1)
def sleep10():
    sleep(10)
    print('slept 10 seconds')

>>> sleep10()
sleep10 took too long         # Note that it hangs here about 9 more seconds
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 11, in inner
  File "<stdin>", line 3, in sleep10
KeyboardInterrupt

명시 적으로 확인하지 않는 한 확장에서 실행되는 코드를 중단하지 않을 수도 PyErr_CheckSignals()있습니다. Cython, Python 및 KeyboardInterrupt를 참조하십시오.

어쨌든 스레드가 1 초 이상 잠자 지 않도록해야합니다. 프로세서 시간이 짧습니다.

함수를 어떻게 호출합니까? 또는 5 초 이상 걸리면 스크립트가 취소하고 다른 작업을 수행하기 위해 무엇을 랩핑 합니까?

그것을 잡아서 다른 것을하기 위해 KeyboardInterrupt를 잡을 수 있습니다.

>>> try:
...     countdown(10)
... except KeyboardInterrupt:
...     print('do something else')
... 
countdown started
10, 9, 8, 7, 6, countdown took too long
do something else

아직 전체 게시물을 읽지 못했지만 플러시가 0이면 어떻게 될까요? 그것은 아래 if 문에서 거짓으로 해석 될 것입니다.
Koenraad van Duin

2
왜 전화해야 thread.interrupt_main()합니까? 왜 직접 예외를 제기 할 수 없습니까?
Anirban Nag 'tintinmj'7

multiprocessing.connection.Client이것 으로 포장에 대한 생각 이 있습니까? -해결하려고 : stackoverflow.com/questions/57817955/…
wwii

51

순수한 함수 (스레딩 제안과 동일한 API 사용)이며 다른 제안이 있습니다 (이 스레드에 대한 제안을 기반으로)

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

3
원래 신호 핸들러도 복원해야합니다. 참조 stackoverflow.com/questions/492519/...
마틴 코네 크니

9
한 가지 더 참고 : 유닉스 신호 방법은 메인 스레드에 적용하는 경우에만 작동합니다. 하위 스레드에 적용하면 예외가 발생하고 작동하지 않습니다.
Martin Konecny ​​2016 년

12
이것은 Linux에서만 작동하기 때문에 최상의 솔루션은 아닙니다.
최대

17
Max, true 아님-POSIX 호환 유닉스에서 작동합니다. 귀하의 의견 이보다 정확해야한다고 생각합니다 .Windows에서는 작동하지 않습니다.
Chris Johnson

6
크워 그를 빈 dict로 설정하지 마십시오. 파이썬의 일반적인 공통점은 함수의 기본 인수가 변경 가능하다는 것입니다. 따라서 사전은에 대한 모든 호출에서 공유됩니다 timeout. 기본값을 설정 None하고 함수의 첫 번째 줄에서 add 를 설정하는 것이 훨씬 좋습니다 kwargs = kwargs or {}. 튜플을 변경할 수 없기 때문에 Args는 괜찮습니다.
scottmrogowski

32

단위 테스트에서 시간 초과 호출을 검색 할 때이 스레드를 가로 질러 실행되었습니다. 답변이나 타사 패키지에서 간단한 것을 찾지 못했기 때문에 아래 코드를 넣을 수있는 데코레이터를 작성했습니다.

import multiprocessing.pool
import functools

def timeout(max_timeout):
    """Timeout decorator, parameter in seconds."""
    def timeout_decorator(item):
        """Wrap the original function."""
        @functools.wraps(item)
        def func_wrapper(*args, **kwargs):
            """Closure for function."""
            pool = multiprocessing.pool.ThreadPool(processes=1)
            async_result = pool.apply_async(item, args, kwargs)
            # raises a TimeoutError if execution exceeds max_timeout
            return async_result.get(max_timeout)
        return func_wrapper
    return timeout_decorator

그런 다음 테스트 또는 원하는 기능을 시간 초과하는 것은 간단합니다.

@timeout(5.0)  # if execution takes longer than 5 seconds, raise a TimeoutError
def test_base_regression(self):
    ...

14
타임 아웃에 도달 한 후에도 기능이 종료되지 않으므로주의하십시오!
Sylvain

Windows에서는 완전히 새로운 프로세스가 생성됩니다.이 프로세스는 종속성을 설정하는 데 시간이 오래 걸리는 경우 시간이 초과되는 시간이 걸릴 수 있습니다.
Aaron Hall

1
예, 약간의 조정이 필요합니다. 스레드는 영원히 계속됩니다.
sudo

2
이것이 가장 좋은 방법이라면 IDK이지만 Exceptionfunc_wrapper 내부에서 시도 / 잡을 수 pool.close()있고 잡은 후에 스레드가 항상 나중에 죽지 않도록 할 수 있습니다. 그런 다음 TimeoutError원하는 것을 던질 수 있습니다 . 나를 위해 일하는 것 같습니다.
sudo

2
이것은 유용하지만 일단 여러 번 수행하면 얻을 수 RuntimeError: can't start new thread있습니다. 무시해도 문제가 해결 되나요? 아니면이 문제를 해결하기 위해 할 수있는 일이 있습니까? 미리 감사드립니다!
벤지

20

stopitpypi에 있는 패키지는 시간 초과를 잘 처리하는 것 같습니다.

나는 @stopit.threading_timeoutable데코레이터를 좋아하는데, 데코레이터 timeout는 데코 레이팅 된 함수에 매개 변수를 추가 합니다.

pypi에서 확인하십시오. https://pypi.python.org/pypi/stopit


1
매우 편리하고 스레드 안전합니다! 감사와 플러스 하나! 이것은 지금까지 내가 찾은 가장 좋은 옵션이며 허용되는 답변보다 낫습니다 !!
Yahya

라이브러리 주장, 일부 기능은 Windows에서 작동하지 않습니다.
Stefan Simik 2016 년

16

많은 제안이 있지만 동시 처리를 사용하는 것은 없습니다.이를 처리하는 가장 읽기 쉬운 방법이라고 생각합니다.

from concurrent.futures import ProcessPoolExecutor

# Warning: this does not terminate function if timeout
def timeout_five(fnc, *args, **kwargs):
    with ProcessPoolExecutor() as p:
        f = p.submit(fnc, *args, **kwargs)
        return f.result(timeout=5)

읽고 유지하기가 매우 간단합니다.

풀을 만들고 단일 프로세스를 제출 한 다음 필요에 따라 잡을 수있는 TimeoutError를 발생시키기 전에 최대 5 초 동안 기다립니다.

파이썬 3.2 이상을 기본으로하며 2.7 (핍 설치 선물)로 백 포트되었습니다.

스레드와 프로세스간에 전환 교체 한 간단하다 ProcessPoolExecutor으로ThreadPoolExecutor 합니다.

시간 초과시 프로세스를 종료하려면 Pebble을 살펴보십시오 .


2
"경고 : 타임 아웃시 기능이 종료되지 않는다"는 것은 무엇을 의미합니까?
Scott Stafford

5
@ScottStafford 프로세스 / 스레드는 TimeoutError가 발생했기 때문에 끝나지 않습니다. 따라서 프로세스 또는 스레드는 여전히 실행을 완료하려고 시도하며 시간 초과시 자동으로 제어를 다시 제공하지 않습니다.
Brian

이 시점에서 중간 결과를 저장할 수 있습니까? 예를 들어 시간 초과를 5로 설정 한 재귀 함수가 있고 그 시간에 부분 결과가있는 경우 시간 초과시 부분 결과를 반환하는 함수를 어떻게 작성합니까?
SumNeuron

정확히 이것을 사용하고 있지만 1000 작업이 있으며 각 작업은 시간 초과 5 초 전에 허용됩니다. 내 문제는 시간 초과가 개별 작업이 아닌 총 작업에만 적용되기 때문에 결코 끝나지 않는 작업에서 코어가 막히는 것입니다. synchronize.futures는이 afaik에 대한 솔루션을 제공하지 않습니다.
Bastiaan

12

훌륭하고 사용하기 쉽고 안정적인 PyPi 프로젝트 시간 초과 장식 자 ( https://pypi.org/project/timeout-decorator/ )

설치 :

pip install timeout-decorator

사용법 :

import time
import timeout_decorator

@timeout_decorator.timeout(5)
def mytest():
    print "Start"
    for i in range(1,10):
        time.sleep(1)
        print "%d seconds have passed" % i

if __name__ == '__main__':
    mytest()

2
명확한 해결책에 감사드립니다. 그러나 누구나이 라이브러리가 어떻게 작동하는지, 특히 멀티 스레딩을 다룰 때 설명 할 수 있습니까? 개인적으로 나는 스레드 또는 신호를 처리하기 위해 알 수없는 메커니즘을 사용하는 것을 두려워합니다.
wsysuper

@wsysuper lib는 2 가지 작동 모드가 있습니다 : 새 스레드 열기 또는 새 하위 프로세스 (스레드 안전하다고 가정)
Gil

이것은 나를 위해 아주 잘 작동했습니다!
Florian Heigl

6

나는 wrapt_timeout_decorator의 저자입니다

여기에 제시된 솔루션의 대부분은 언뜻보기에 Linux에서 우연히 작동합니다. fork () 및 signal ()이 있기 때문입니다. 그러나 창에서는 상황이 약간 다르게 보입니다. 그리고 Linux의 하위 스레드와 관련하여 더 이상 신호를 사용할 수 없습니다.

Windows에서 프로세스를 스폰하려면 선택 가능해야하며 많은 데코 레이팅 된 함수 나 클래스 메소드는 그렇지 않습니다.

따라서 딜 및 멀티 프로세스 (피클 및 멀티 프로세싱 아님)와 같은 더 나은 피커를 사용해야합니다. 따라서 ProcessPoolExecutor (또는 제한된 기능 만 사용)를 사용할 수 없습니다.

시간 종료 자체의 경우-시간 종료의 의미를 정의해야합니다. Windows에서는 프로세스를 생성하는 데 상당한 (결정 불가능한) 시간이 걸리기 때문입니다. 시간이 짧으면 까다로울 수 있습니다. 프로세스 생성에 약 0.5 초 (쉽게 !!!)가 걸린다고 가정 해 봅시다. 0.2 초의 타임 아웃을 주면 어떻게됩니까? 0.5 + 0.2 초 후에 함수가 시간 초과되어야합니까 (그래서 0.2 초 동안 메소드를 실행 시키십시오)? 또는 호출 된 프로세스가 0.2 초 후에 시간 초과되어야합니까 (이 경우 데코레이션 된 함수는 항상 생성되지 않기 때문에 시간 초과됩니다)?

또한 중첩 데코레이터가 불쾌 할 수 있으며 서브 스레드에서 신호를 사용할 수 없습니다. 진정으로 보편적 인 크로스 플랫폼 데코레이터를 만들려면이 모든 것을 고려하고 테스트해야합니다.

다른 문제는 예외를 호출자에게 다시 전달하고 로깅 문제를 해결합니다 (장식 된 함수에서 사용되는 경우 다른 프로세스의 파일에 로깅하는 것은 지원되지 않음)

나는 모든 엣지 케이스를 다루려고 노력했다. wrapt_timeout_decorator 패키지를 보거나 적어도 거기에서 사용 된 단위 테스트에서 영감을 얻은 자신의 솔루션을 테스트 할 수 있습니다.

@Alexis Eggermont-안타깝게도 의견을 말할 충분한 포인트가 없습니다. 다른 사람이 귀하에게 알릴 수 있습니다-수입 문제를 해결했다고 생각합니다.


3

timeout-decoratorWindows 시스템에서 작동하지 않으므로 Windows가 제대로 지원하지 않았습니다 signal.

Windows 시스템에서 시간 초과 장식자를 사용하면 다음과 같은 결과가 나타납니다.

AttributeError: module 'signal' has no attribute 'SIGALRM'

일부는 사용을 제안 use_signals=False했지만 나에게 효과가 없었습니다.

@bitranox는 다음 패키지를 만들었습니다.

pip install https://github.com/bitranox/wrapt-timeout-decorator/archive/master.zip

코드 샘플 :

import time
from wrapt_timeout_decorator import *

@timeout(5)
def mytest(message):
    print(message)
    for i in range(1,10):
        time.sleep(1)
        print('{} seconds have passed'.format(i))

def main():
    mytest('starting')


if __name__ == '__main__':
    main()

다음 예외를 제공합니다.

TimeoutError: Function mytest timed out after 5 seconds

이것은 매우 좋은 해결책처럼 들립니다. 이상하게도이 라인 from wrapt_timeout_decorator import * 은 다른 수입품을 죽이는 것 같습니다. 예를 들어 ModuleNotFoundError: No module named 'google.appengine',을 얻었지만 wrapt_timeout_decorator를 가져 오지 않으면이 오류가 발생하지 않습니다.
Alexis Eggermont

@ AlexisEggermont 나는 이것을 appengine과 함께 사용하려고했다 ... 그래서이 오류가 계속된다면 매우 궁금하다?
PascalVKooten

2

같은 신호를 사용할 수 있습니다. 아래 예제가 도움이 될 것이라고 생각합니다. 스레드에 비해 매우 간단합니다.

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"

1
특정 예외를 선택하고 예외 만 잡는 것이 좋습니다. 맨손 try: ... except: ...은 항상 나쁜 생각입니다.
고용

나는 당신이 하이킹에 동의합니다.
AR

나는 그 이유를 이해하지만, sysadmin / integrator로서 동의하지 않습니다. 파이썬 코드는 오류 처리를 무시하고 악의적 인 것으로 처리하는 것으로 악명이 높습니다. 품질 소프트웨어에는 충분하지 않습니다. 계획 한 5 가지 사항과 다른 것들에 대한 일반적인 전략을 처리 할 수 ​​있습니다. "추적, 없음"은 전략이 아니며 모욕입니다.
Florian Heigl

2
#!/usr/bin/python2
import sys, subprocess, threading
proc = subprocess.Popen(sys.argv[2:])
timer = threading.Timer(float(sys.argv[1]), proc.terminate)
timer.start()
proc.wait()
timer.cancel()
exit(proc.returncode)

7
이 코드는 질문에 대한 답변을 제공 할 수 있지만, 문제를 해결하는 방법 및 / 또는 이유에 대한 추가 컨텍스트를 제공하면 답변의 장기적 가치를 향상시킬 수 있습니다.
Dan Cornilescu

1

나는이 필요했다 중첩 가능한 (스레드 기반 접근 할 수없는) time.sleep에 의해 차단되지 않습니다 시간 초과 인터럽트 (SIGALARM 할 수없는). 나는 여기에서 코드를 복사하고 가볍게 수정했다 : http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/

코드 자체 :

#!/usr/bin/python

# lightly modified version of http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/


"""alarm.py: Permits multiple SIGALRM events to be queued.

Uses a `heapq` to store the objects to be called when an alarm signal is
raised, so that the next alarm is always at the top of the heap.
"""

import heapq
import signal
from time import time

__version__ = '$Revision: 2539 $'.split()[1]

alarmlist = []

__new_alarm = lambda t, f, a, k: (t + time(), f, a, k)
__next_alarm = lambda: int(round(alarmlist[0][0] - time())) if alarmlist else None
__set_alarm = lambda: signal.alarm(max(__next_alarm(), 1))


class TimeoutError(Exception):
    def __init__(self, message, id_=None):
        self.message = message
        self.id_ = id_


class Timeout:
    ''' id_ allows for nested timeouts. '''
    def __init__(self, id_=None, seconds=1, error_message='Timeout'):
        self.seconds = seconds
        self.error_message = error_message
        self.id_ = id_
    def handle_timeout(self):
        raise TimeoutError(self.error_message, self.id_)
    def __enter__(self):
        self.this_alarm = alarm(self.seconds, self.handle_timeout)
    def __exit__(self, type, value, traceback):
        try:
            cancel(self.this_alarm) 
        except ValueError:
            pass


def __clear_alarm():
    """Clear an existing alarm.

    If the alarm signal was set to a callable other than our own, queue the
    previous alarm settings.
    """
    oldsec = signal.alarm(0)
    oldfunc = signal.signal(signal.SIGALRM, __alarm_handler)
    if oldsec > 0 and oldfunc != __alarm_handler:
        heapq.heappush(alarmlist, (__new_alarm(oldsec, oldfunc, [], {})))


def __alarm_handler(*zargs):
    """Handle an alarm by calling any due heap entries and resetting the alarm.

    Note that multiple heap entries might get called, especially if calling an
    entry takes a lot of time.
    """
    try:
        nextt = __next_alarm()
        while nextt is not None and nextt <= 0:
            (tm, func, args, keys) = heapq.heappop(alarmlist)
            func(*args, **keys)
            nextt = __next_alarm()
    finally:
        if alarmlist: __set_alarm()


def alarm(sec, func, *args, **keys):
    """Set an alarm.

    When the alarm is raised in `sec` seconds, the handler will call `func`,
    passing `args` and `keys`. Return the heap entry (which is just a big
    tuple), so that it can be cancelled by calling `cancel()`.
    """
    __clear_alarm()
    try:
        newalarm = __new_alarm(sec, func, args, keys)
        heapq.heappush(alarmlist, newalarm)
        return newalarm
    finally:
        __set_alarm()


def cancel(alarm):
    """Cancel an alarm by passing the heap entry returned by `alarm()`.

    It is an error to try to cancel an alarm which has already occurred.
    """
    __clear_alarm()
    try:
        alarmlist.remove(alarm)
        heapq.heapify(alarmlist)
    finally:
        if alarmlist: __set_alarm()

사용 예 :

import alarm
from time import sleep

try:
    with alarm.Timeout(id_='a', seconds=5):
        try:
            with alarm.Timeout(id_='b', seconds=2):
                sleep(3)
        except alarm.TimeoutError as e:
            print 'raised', e.id_
        sleep(30)
except alarm.TimeoutError as e:
    print 'raised', e.id_
else:
    print 'nope.'

또한 스레드에서 호출하면 신호를 사용하므로 작동하지 않습니다.
garg10may

0

주어진 스레드 기반 솔루션이 약간 개선되었습니다.

아래 코드는 예외를 지원합니다 .

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]

5 초 시간 초과로 호출 :

result = timeout(remote_calculate, (myarg,), timeout_duration=5)

1
원래 트레이스 백을 숨기는 새로운 예외가 발생합니다. 아래 내 버전을 참조하십시오 ...
Meitham

1
runFunctionCatchExceptions()특정 파이썬 함수 내에서 GIL을 얻는 것처럼 호출 되는 것처럼 안전하지 않습니다. 예를 들어, 함수 내에서 호출되면 다음이 반환되지 않거나 아주 오랫동안 반환되지 않습니다 eval(2**9999999999**9999999999). 참조 stackoverflow.com/questions/22138190/...
미코 Ohtamaa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.