답변:
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
signal.alarm
에서는 관련 기능 SIGALRM
을 사용할 수 없습니다.
signal.signal
--- 모두 제대로 작동합니까? 각 signal.signal
호출이 "동시"호출을 취소 하지 않습니까?
당신이 사용할 수있는 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()
join()
. 그것은 x 개의 동시 서브 프로세스가 작업을 완료 할 때까지 또는 실행중인 양을 정의 할 때까지 실행됩니다 join(10)
. 10 개의 프로세스에 대해 차단 I / O가있는 경우 join (10)을 사용하여 시작된 각 프로세스에 대해 최대 10까지 대기하도록 설정했습니다. 이 예제와 같은 데몬 플래그를 사용하십시오 . stackoverflow.com/a/27420072/2480481 물론 u는 플래그 daemon=True
를 multiprocessing.Process()
함수에 직접 전달할 수 있습니다 .
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.
함수를 어떻게 호출하거나 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
thread.interrupt_main()
합니까? 왜 직접 예외를 제기 할 수 없습니까?
multiprocessing.connection.Client
이것 으로 포장에 대한 생각 이 있습니까? -해결하려고 : stackoverflow.com/questions/57817955/…
순수한 함수 (스레딩 제안과 동일한 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
timeout
. 기본값을 설정 None
하고 함수의 첫 번째 줄에서 add 를 설정하는 것이 훨씬 좋습니다 kwargs = kwargs or {}
. 튜플을 변경할 수 없기 때문에 Args는 괜찮습니다.
단위 테스트에서 시간 초과 호출을 검색 할 때이 스레드를 가로 질러 실행되었습니다. 답변이나 타사 패키지에서 간단한 것을 찾지 못했기 때문에 아래 코드를 넣을 수있는 데코레이터를 작성했습니다.
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):
...
Exception
func_wrapper 내부에서 시도 / 잡을 수 pool.close()
있고 잡은 후에 스레드가 항상 나중에 죽지 않도록 할 수 있습니다. 그런 다음 TimeoutError
원하는 것을 던질 수 있습니다 . 나를 위해 일하는 것 같습니다.
RuntimeError: can't start new thread
있습니다. 무시해도 문제가 해결 되나요? 아니면이 문제를 해결하기 위해 할 수있는 일이 있습니까? 미리 감사드립니다!
stopit
pypi에 있는 패키지는 시간 초과를 잘 처리하는 것 같습니다.
나는 @stopit.threading_timeoutable
데코레이터를 좋아하는데, 데코레이터 timeout
는 데코 레이팅 된 함수에 매개 변수를 추가 합니다.
pypi에서 확인하십시오. https://pypi.python.org/pypi/stopit
많은 제안이 있지만 동시 처리를 사용하는 것은 없습니다.이를 처리하는 가장 읽기 쉬운 방법이라고 생각합니다.
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을 살펴보십시오 .
훌륭하고 사용하기 쉽고 안정적인 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()
나는 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-안타깝게도 의견을 말할 충분한 포인트가 없습니다. 다른 사람이 귀하에게 알릴 수 있습니다-수입 문제를 해결했다고 생각합니다.
timeout-decorator
Windows 시스템에서 작동하지 않으므로 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를 가져 오지 않으면이 오류가 발생하지 않습니다.
같은 신호를 사용할 수 있습니다. 아래 예제가 도움이 될 것이라고 생각합니다. 스레드에 비해 매우 간단합니다.
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"
try: ... except: ...
은 항상 나쁜 생각입니다.
#!/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)
나는이 필요했다 중첩 가능한 (스레드 기반 접근 할 수없는) 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.'
주어진 스레드 기반 솔루션이 약간 개선되었습니다.
아래 코드는 예외를 지원합니다 .
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)
runFunctionCatchExceptions()
특정 파이썬 함수 내에서 GIL을 얻는 것처럼 호출 되는 것처럼 안전하지 않습니다. 예를 들어, 함수 내에서 호출되면 다음이 반환되지 않거나 아주 오랫동안 반환되지 않습니다 eval(2**9999999999**9999999999)
. 참조 stackoverflow.com/questions/22138190/...