Yanıtlar:
UNIX üzerinde çalışıyorsanız sinyal paketini kullanabilirsiniz :
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
Aramadan 10 saniye sonra alarm.alarm(10)
işleyici çağrılır. Bu, normal Python kodundan kesebileceğiniz bir istisna oluşturur.
Bu modül iş parçacığı ile iyi oynamıyor (ama sonra kim oynuyor?)
Not o zaman aşımı meydana geldiğinde bir istisna yükseltmek zamandan beri, böyle bir fonksiyonun örneğin yakalanarak işlevi içinde göz ardı sona erebilir:
def loop_forever():
while 1:
print('sec')
try:
time.sleep(10)
except:
continue
signal.alarm
ve ilgili SIGALRM
Windows platformlarında mevcut değildir.
signal.signal
--- hepsi düzgün çalışır mı? Her signal.signal
çağrı "eşzamanlı" bir çağrıyı iptal etmeyecek mi?
multiprocessing.Process
Tam olarak bunu yapmak için kullanabilirsiniz .
kod
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 eşzamanlı alt işleminizin çalışmasını bitirinceye veya çalışmakta olan miktara kadar çalışmasını sağlar join(10)
. 10 işlem için engelleme G / Ç'niz olması durumunda, birleştirme (10) kullanarak, bunların tümünü başlayan her bir işlem için en fazla 10 beklemesini ayarladınız. Bu örnek gibi daemon bayrağını kullanın stackoverflow.com/a/27420072/2480481 . Tabii ki u fonksiyon daemon=True
doğrudan bayrak geçirebilirsiniz 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.
İşlevi nasıl arayabilirim veya 5 saniyeden uzun sürerse komut dosyasının iptal etmesi için onu ne içine sarabilirim?
Ben yayınlanmıştır özünü o çözer bir dekoratör ve bu soru / sorun threading.Timer
. Burada bir arıza var.
Python 2 ve 3 ile test edildi. Ayrıca Unix / Linux ve Windows altında da çalışmalıdır.
İlk olarak ithalat. Bunlar Python sürümüne bakılmaksızın kodu tutarlı tutmaya çalışır:
from __future__ import print_function
import sys
import threading
from time import sleep
try:
import thread
except ImportError:
import _thread as thread
Sürümden bağımsız kod kullanın:
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
Şimdi işlevselliğimizi standart kütüphaneden içe aktardık.
exit_after
dekoratörSonra main()
alt iş parçacığından sonlandırmak için bir işleve ihtiyacımız var :
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
Ve işte dekoratörün kendisi:
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
Ve işte 5 saniye sonra çıkma ile ilgili sorunuza doğrudan cevap veren kullanım !:
@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')
Demo:
>>> 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
İkinci işlev çağrısı bitmez, bunun yerine işlem bir geri izleme ile çıkmalıdır!
KeyboardInterrupt
uyku ipliğini her zaman durdurmazUyku durumunun Windows'ta Python 2'de klavye kesintisi ile her zaman kesintiye uğramayacağını unutmayın, örn:
@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
ne de açıkça kontrol etmedikçe uzantılarda çalışan kodu kesmesi olası değildir PyErr_CheckSignals()
, bkz. Cython, Python ve KeyboardInterrupt yok sayıldı
Her durumda, bir iplik bir saniyeden fazla uyumaktan kaçınırım - bu işlemci zamanında bir eon.
İşlevi nasıl çağırırım veya 5 saniyeden uzun sürerse komut dosyasının iptal etmesi ve başka bir şey yapması için ne sararım?
Yakalamak ve başka bir şey yapmak için KeyboardInterrupt'ı yakalayabilirsiniz.
>>> 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()
, neden doğrudan bir istisna oluşturamıyorum?
multiprocessing.connection.Client
mı? - Çözmeye çalışıyorum: stackoverflow.com/questions/57817955/…
Saf bir işlevi olan (iş parçacığı öneri ile aynı API ile) ve (bu iş parçacığı önerileri dayalı) iyi çalışıyor gibi görünüyor farklı bir teklif var
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
. Varsayılanı ayarlamak None
ve işlevin ilk satırında ekleme yapmak çok daha iyidir kwargs = kwargs or {}
. Args iyi çünkü tuples değişmez değil.
Birim testlerinde bir zaman aşımı çağrısı ararken bu iş parçacığında koştum. Yanıtları veya 3. parti paketlerinde basit bir şey bulamadım, bu yüzden aşağıdaki dekoratörü yazdınız, hemen koda bırakabilirsiniz:
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
Sonra bir testi veya istediğiniz herhangi bir işlevi zaman aşımı yapmak için bu kadar basit:
@timeout(5.0) # if execution takes longer than 5 seconds, raise a TimeoutError
def test_base_regression(self):
...
Exception
yolsa , ancak func_wrapper'ın içinde deneyebilir / yakalayabilir pool.close()
ve yakalamadan sonra ne olursa olsun ipliğin her zaman sonra ölmesini sağlamak için yapabilirsiniz. Sonra TimeoutError
ya da sonra ne istersen atabilirsin . Benim için iş gibi görünüyor.
RuntimeError: can't start new thread
. Eğer görmezden gelirsem işe yarayacak mı yoksa bu sorunu aşmak için yapabileceğim başka bir şey var mı? Şimdiden teşekkürler!
stopit
Pypi bulunan paket, iyi zaman aşımına işlemek gibi görünüyor.
Ben dekore ne beklediğiniz ne yapar, @stopit.threading_timeoutable
bir timeout
parametre ekler dekoratör gibi, işlevi durdurur.
Pypi'ye göz atın: https://pypi.python.org/pypi/stopit
Çok fazla öneri var, ama hiçbiri concurrent.futures kullanmıyor, bence bu işin en okunaklı yolu.
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)
Okuması ve bakımı süper basit.
Bir havuz oluşturuyoruz, tek bir işlem gönderiyoruz ve sonra ihtiyacınız olan her şeyi yakalayabileceğiniz ve işleyebileceğiniz bir TimeoutError'u yükseltmeden önce 5 saniye bekleyin.
Python 3.2+'ye özgüdür ve 2.7'ye geri bildirilir (pip install futures).
İş parçacıkları ve işlemler arasında geçiş yapmak ProcessPoolExecutor
,ThreadPoolExecutor
.
Süreci zaman aşımına uğratmak istiyorsanız Çakıl taşına bakmanızı öneririm .
Harika, kullanımı kolay ve güvenilir PyPi projesi zaman aşımı-dekoratör ( https://pypi.org/project/timeout-decorator/ )
kurulum :
pip install timeout-decorator
Kullanımı :
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 yazarıyım
Burada sunulan çözümlerin çoğu ilk bakışta Linux altında harika çalışıyor - çünkü fork () ve sinyaller () var - ama pencerelerde işler biraz farklı görünüyor. Ve Linux'ta altyazılar söz konusu olduğunda, artık Sinyalleri kullanamazsınız.
Bir işlemi Windows altında oluşturmak için seçilebilir olması gerekir - ve birçok süslü işlev veya Sınıf yöntemi değildir.
Bu yüzden dereotu ve çok işlemli (turşu ve çoklu işleme değil) gibi daha iyi bir toplayıcı kullanmanız gerekir - bu yüzden ProcessPoolExecutor'u (veya sadece sınırlı işlevsellikle) kullanamazsınız.
Zaman aşımının kendisi için - Zaman aşımının ne anlama geldiğini tanımlamanız gerekir - çünkü Windows'da süreci ortaya çıkarmak için önemli (ve belirlenemez değil) zaman alacaktır. Kısa zaman aşımlarında bu zor olabilir. Varsayalım, sürecin ortaya çıkması yaklaşık 0,5 saniye sürüyor (kolayca !!!). Eğer 0.2 saniyelik bir zaman aşımı verirseniz ne olur? İşlev 0,5 + 0,2 saniye sonra zaman aşımına uğramalı mı (yöntemi 0,2 saniye çalıştırsın)? Ya da çağrılan işlem 0.2 saniye sonra zaman aşımına uğrar (bu durumda, dekore edilmiş fonksiyon HER ZAMAN zaman aşımına uğrar, çünkü o zaman yumurtlanmaz)
Ayrıca iç içe dekoratörler kötü olabilir ve bir alt dizide Sinyalleri kullanamazsınız. Gerçekten evrensel, platformlar arası bir dekoratör oluşturmak istiyorsanız, tüm bunların dikkate alınması (ve test edilmesi) gerekir.
Diğer sorunlar, istisnaları arayana geri aktarmanın yanı sıra günlüğe kaydetme sorunlarıdır (süslü işlevde kullanılıyorsa - başka bir işlemdeki dosyalara günlük kaydı desteklenmez)
Tüm kenar vakaları kapsamayı denedim, wrapt_timeout_decorator paketine bakabilir veya en azından orada kullanılan birim testlerden esinlenerek kendi çözümlerinizi test edebilirsiniz.
@Alexis Eggermont - ne yazık ki yorum yapmak için yeterli puanım yok - belki başka biri sizi bilgilendirebilir - Sanırım ithalat sorununuzu çözdüm.
timeout-decorator
windows sisteminde çalışmaz, windows desteklemiyordu signal
iyi .
Windows sisteminde timeout-decorator kullanırsanız aşağıdakileri alırsınız
AttributeError: module 'signal' has no attribute 'SIGALRM'
Bazılarının kullanılması önerildi use_signals=False
ama benim için çalışmadı.
Author @bitranox aşağıdaki paketi yarattı:
pip install https://github.com/bitranox/wrapt-timeout-decorator/archive/master.zip
Kod Örneği:
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()
Aşağıdaki istisnayı verir:
TimeoutError: Function mytest timed out after 5 seconds
from wrapt_timeout_decorator import *
diğer ithalatlarımın bazılarını öldürüyor gibi görünüyor. Örneğin anladım ModuleNotFoundError: No module named 'google.appengine'
, ancak wrapt_timeout_decorator'ı içe aktarmazsam bu hatayı almıyorum
Aynı sinyalleri kullanabiliriz. Aşağıdaki örneğin sizin için yararlı olacağını düşünüyorum. İpliklere kıyasla çok basit.
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: ...
her zaman kötü bir fikirdir.
#!/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)
Ben ihtiyaç vardı nestable (iplik dayalı yaklaşım yapamaz olan) time.sleep tarafından bloke almazsınız zamanlanmış kesmeleri (SIGALARM yapamaz olan). Buradan kodu kopyalayıp hafifçe değiştirdim: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/
Kodun kendisi:
#!/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()
ve bir kullanım örneği:
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.'
İşte verilen iplik bazlı çözümde küçük bir gelişme.
Aşağıdaki kod istisnaları destekler :
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 saniyelik bir zaman aşımı ile çağırmak:
result = timeout(remote_calculate, (myarg,), timeout_duration=5)
runFunctionCatchExceptions()
GIL elde etmek için belirli Python fonksiyonları içinde çağrılır. Örneğin şu would never veya çok uzun süre, işlev içinde denir eğer dönüş: eval(2**9999999999**9999999999)
. Bkz. Stackoverflow.com/questions/22138190/…