Wywołuję funkcję w Pythonie, która, jak wiem, może się zawiesić i zmusić mnie do ponownego uruchomienia skryptu.
Jak wywołać funkcję lub w co ją owinąć, aby skoro trwa dłużej niż 5 sekund, skrypt ją anuluje i zrobi coś innego?
Wywołuję funkcję w Pythonie, która, jak wiem, może się zawiesić i zmusić mnie do ponownego uruchomienia skryptu.
Jak wywołać funkcję lub w co ją owinąć, aby skoro trwa dłużej niż 5 sekund, skrypt ją anuluje i zrobi coś innego?
Możesz użyć pakietu sygnału , jeśli pracujesz w systemie 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 sekund po wywołaniu alarm.alarm(10)
zostaje wywołany przewodnik. Rodzi to wyjątek, który można przechwycić ze zwykłego kodu Pythona.
Ten moduł nie działa dobrze z wątkami (ale w takim razie, kto?)
Pamiętaj, że ponieważ zgłaszamy wyjątek, gdy nastąpi przekroczenie limitu czasu, może on zostać złapany i zignorowany wewnątrz funkcji, na przykład jednej z takich funkcji:
def loop_forever():
while 1:
print('sec')
try:
time.sleep(10)
except:
continue
signal.alarm
i powiązaneSIGALRM
nie są dostępne na platformach Windows.signal.signal
--- czy wszystkie działają poprawnie? Czy każdesignal.signal
połączenie nie anuluje „jednoczesnego” jednego?Możesz użyć
multiprocessing.Process
do zrobienia dokładnie tego.Kod
źródło
join()
. to sprawia, że twoja liczba współbieżnych podprocesów jest uruchomiona, dopóki nie zakończą pracy, lub ilość zdefiniowana wjoin(10)
. Jeśli masz blokujące operacje we / wy dla 10 procesów, używając join (10) ustawiłeś je tak, aby czekały na maksimum 10 dla KAŻDEGO rozpoczętego procesu. Użyj flagi demona, jak w tym przykładzie stackoverflow.com/a/27420072/2480481 . Oczywiście możesz przekazać flagędaemon=True
bezpośrednio domultiprocessing.Process()
funkcji.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.
Zamieściłem istotę , która rozwiązuje to pytanie / problem z dekorator a
threading.Timer
. Oto podział.Importuje i konfiguruje dla kompatybilności
Został przetestowany w Pythonie 2 i 3. Powinien także działać w systemach Unix / Linux i Windows.
Najpierw import. Starają się zachować spójność kodu niezależnie od wersji Pythona:
Użyj kodu niezależnego od wersji:
Teraz zaimportowaliśmy naszą funkcjonalność ze standardowej biblioteki.
exit_after
dekoratorNastępnie potrzebujemy funkcji, aby zakończyć
main()
wątek potomny:A oto sam dekorator:
Stosowanie
A oto użycie, które bezpośrednio odpowiada na pytanie o wyjściu po 5 sekundach:
Próbny:
Drugie wywołanie funkcji nie zakończy się, zamiast tego proces powinien zostać zakończony za pomocą śledzenia zwrotnego!
KeyboardInterrupt
nie zawsze zatrzymuje śpiący wątekPamiętaj, że uśpienie nie zawsze będzie przerywane przerwaniem klawiatury, w Pythonie 2 w systemie Windows, np .:
nie jest też w stanie przerwać działania kodu w rozszerzeniach, chyba że wyraźnie to sprawdzi
PyErr_CheckSignals()
, zobacz Cython, Python i KeyboardInterrupt ignorowaneW każdym razie unikałbym spania wątku dłużej niż sekundę - to eon czasu procesora.
Aby go złapać i zrobić coś innego, możesz złapać KeyboardInterrupt.
źródło
thread.interrupt_main()
, dlaczego nie mogę bezpośrednio zgłosić wyjątku?multiprocessing.connection.Client
tego? - Próbuję rozwiązać: stackoverflow.com/questions/57817955/…Mam inną propozycję, która jest czystą funkcją (z tym samym interfejsem API co sugestia wątków) i wydaje się działać dobrze (w oparciu o sugestie dotyczące tego wątku)
źródło
timeout
. O wiele lepiej jest ustawić wartość domyślnąNone
i dodać w pierwszym wierszu funkcjikwargs = kwargs or {}
. Args jest w porządku, ponieważ krotek nie można modyfikować.Natknąłem się na ten wątek, szukając limitu czasu w testach jednostkowych. Nie znalazłem nic prostego w odpowiedziach ani paczkach stron trzecich, więc napisałem poniżej dekorator, który możesz wrzucić do kodu:
To jest tak proste, jak przekroczenie limitu czasu testu lub dowolnej funkcji, którą lubisz:
źródło
Exception
wewnątrz func_wrapper i zrobićpool.close()
po złapaniu, aby upewnić się, że wątek zawsze umiera później, bez względu na wszystko. Następnie możesz rzucićTimeoutError
lub cokolwiek chcesz. Wydaje się dla mnie pracować.RuntimeError: can't start new thread
. Czy nadal będzie działać, jeśli go zignoruję, czy jest coś innego, co mogę zrobić, aby to obejść? Z góry dziękuję!stopit
Pakiet, znalezionych na PyPI, wydaje się dobrze obsłużyć limity czasu.Podoba mi się
@stopit.threading_timeoutable
dekorator, który dodajetimeout
parametr do dekorowanej funkcji, która robi to, czego oczekujesz, zatrzymuje funkcję.Sprawdź to na pypi: https://pypi.python.org/pypi/stopit
źródło
Istnieje wiele sugestii, ale żadna z nich nie używa współbieżnych. Przyszłości, co moim zdaniem jest najbardziej czytelnym sposobem na poradzenie sobie z tym.
Super prosty do odczytania i utrzymania.
Tworzymy pulę, przesyłamy pojedynczy proces, a następnie czekamy do 5 sekund przed podniesieniem błędu limitu czasu, który można złapać i obsłużyć w razie potrzeby.
Natywny dla Pythona 3.2+ i backportowany do 2.7 (futures instalujacy pip).
Przełączanie pomiędzy gwintem i sposobów jest to tak proste jak wymiana
ProcessPoolExecutor
zThreadPoolExecutor
.Jeśli chcesz zakończyć Proces po upływie limitu czasu, sugeruję zajrzenie do Pebble .
źródło
Świetny, łatwy w użyciu i niezawodny dekorator przekroczenia limitu czasu projektu PyPi ( https://pypi.org/project/timeout-decorator/ )
instalacja :
Zastosowanie :
źródło
Jestem autorem wrapt_timeout_decorator
Większość przedstawionych tutaj rozwiązań działa na pierwszy rzut oka pod Linuksem - ponieważ mamy fork () i sygnały () - ale w systemie Windows wygląda to nieco inaczej. A jeśli chodzi o wątki w Linuksie, nie możesz już używać Sygnałów.
Aby odrodzić proces w systemie Windows, musi być możliwy do odebrania - a wiele udekorowanych funkcji lub metod klasowych nie.
Musisz więc użyć lepszego piklera, takiego jak koperek i proces wieloprocesowy (nie marynowany i wieloprocesowy) - dlatego nie możesz używać ProcessPoolExecutor (lub tylko z ograniczoną funkcjonalnością).
Dla samego limitu czasu - Musisz zdefiniować, co oznacza limit czasu - ponieważ w systemie Windows zajmie to sporo (i nie można go określić) czasu na odrodzenie procesu. Może to być trudne w przypadku krótkich limitów czasu. Załóżmy, że odrodzenie procesu zajmuje około 0,5 sekundy (łatwo !!!). Jeśli limit czasu wynosi 0,2 sekundy, co powinno się stać? Czy funkcja powinna upłynąć po 0,5 + 0,2 sekundy (więc niech metoda będzie działać przez 0,2 sekundy)? A może wywoływany proces wygaśnie po 0,2 sekundy (w takim przypadku funkcja dekoracyjna ZAWSZE przekroczy limit czasu, ponieważ w tym czasie nie jest nawet odradzana)?
Również zagnieżdżone dekoratory mogą być nieprzyjemne i nie można używać sygnałów w podtytule. Jeśli chcesz stworzyć prawdziwie uniwersalny, wieloplatformowy dekorator, wszystko to należy wziąć pod uwagę (i przetestować).
Inne problemy to przekazywanie wyjątków z powrotem do programu wywołującego, a także problemy z logowaniem (jeśli są używane w funkcji dekorowanej - logowanie do plików w innym procesie NIE jest obsługiwane)
Próbowałem objąć wszystkie przypadki brzegowe. Możesz zajrzeć do pakietu wrapt_timeout_decorator, lub przynajmniej przetestować własne rozwiązania inspirowane najbardziej używanymi tam jednostkami.
@Alexis Eggermont - niestety nie mam wystarczającej liczby punktów do skomentowania - może ktoś inny może Cię powiadomić - myślę, że rozwiązałem problem z importem.
źródło
timeout-decorator
nie działają w systemie Windows, ponieważ Windows nie obsługiwałsignal
dobrze.Jeśli użyjesz timeout-decorator w systemie Windows, otrzymasz następujące
Niektórzy sugerowali użycie,
use_signals=False
ale nie działali dla mnie.Autor @bitranox utworzył następujący pakiet:
Przykładowy kod:
Daje następujący wyjątek:
źródło
from wrapt_timeout_decorator import *
wydaje się zabijać część moich innych importów. Na przykład dostajęModuleNotFoundError: No module named 'google.appengine'
, ale nie dostaję tego błędu, jeśli nie zaimportuję wrapt_timeout_decoratorMożemy użyć sygnałów do tego samego. Myślę, że poniższy przykład będzie dla ciebie przydatny. Jest bardzo prosty w porównaniu do wątków.
źródło
try: ... except: ...
są zawsze złym pomysłem.źródło
Miałem potrzebę gniazdowa przerwania czasowe (który SIGALARM nie można zrobić), które nie będą blokowane przez time.sleep (których podejście oparte wątek nie może zrobić). Skończyłem kopiowanie i lekką modyfikację kodu stąd: http://code.activestate.com/recipes/577600-queue-for-managing-multiple-sigalrm-alarms-concurr/
Sam kod:
i przykład użycia:
źródło
Oto niewielka poprawa podanego rozwiązania opartego na wątkach.
Poniższy kod obsługuje wyjątki :
Wywoływanie go z 5-sekundowym limitem czasu:
źródło
runFunctionCatchExceptions()
niektórych funkcjach Pythona wywoływane są GIL. Np dodaje będzie nigdy, albo za bardzo długi czas powrotu nazywany wewnątrz funkcji:eval(2**9999999999**9999999999)
. Zobacz stackoverflow.com/questions/22138190/…