Jak mogę obsłużyć zdarzenia KeyboardInterrupt z wieloprocesorowymi pulami Pythona? Oto prosty przykład:
from multiprocessing import Pool
from time import sleep
from sys import exit
def slowly_square(i):
sleep(1)
return i*i
def go():
pool = Pool(8)
try:
results = pool.map(slowly_square, range(40))
except KeyboardInterrupt:
# **** THIS PART NEVER EXECUTES. ****
pool.terminate()
print "You cancelled the program!"
sys.exit(1)
print "\nFinally, here are the results: ", results
if __name__ == "__main__":
go()
Podczas uruchamiania powyższego kodu KeyboardInterrupt
zostaje podniesiony po naciśnięciu ^C
, ale proces po prostu zawiesza się w tym momencie i muszę go zewnętrznie zabić.
Chcę mieć możliwość naciśnięcia ^C
w dowolnym momencie i spowodowania, aby wszystkie procesy zakończyły się z wdziękiem.
python
multiprocessing
pool
keyboardinterrupt
Fragsworth
źródło
źródło
Odpowiedzi:
To jest błąd Pythona. Podczas oczekiwania na warunek w threading.Condition.wait (), KeyboardInterrupt nigdy nie jest wysyłany. Repro:
Wyjątek KeyboardInterrupt nie zostanie dostarczony do momentu powrotu wait () i nigdy nie powróci, więc przerwanie nigdy się nie dzieje. KeyboardInterrupt prawie na pewno powinno przerwać warunek oczekiwania.
Zauważ, że tak się nie dzieje, jeśli określono limit czasu; cond.wait (1) natychmiast otrzyma przerwanie. Tak więc obejściem jest określenie limitu czasu. Aby to zrobić, wymień
z
lub podobne.
źródło
Z tego, co ostatnio odkryłem, najlepszym rozwiązaniem jest skonfigurowanie procesów roboczych tak, aby całkowicie ignorowały SIGINT i ograniczyły cały kod czyszczący do procesu nadrzędnego. Rozwiązuje to problem zarówno w przypadku bezczynnych, jak i zajętych procesów roboczych i nie wymaga żadnego kodu obsługi błędów w procesach podrzędnych.
Wyjaśnienie i pełny przykładowy kod można znaleźć odpowiednio pod adresem http://noswap.com/blog/python-multiprocessing-keyboardinterrupt/ i http://github.com/jreese/multiprocessing-keyboardinterrupt .
źródło
time.sleep(10)
głównym procesem. Jeśli miałbyś usunąć to uśpienie lub jeśli czekasz, aż proces spróbuje dołączyć do puli, co musisz zrobić, aby zagwarantować ukończenie zadań, nadal cierpisz na ten sam problem, którego nie robi główny proces nie otrzyma polecenia KeyboardInterrupt podczas oczekiwania najoin
operację sondowania .pool.terminate()
nigdy nie zostaje stracony. Ignorowanie sygnału przez dzieci nic nie daje. Odpowiedź @ Glenna rozwiązuje problem..join()
z wyjątkiem przerwania - po prostu ręcznie sprawdza wynik.apply_async()
użycia,AsyncResult.ready()
aby zobaczyć, czy jest gotowy, co oznacza, że skończyliśmy czysto.Z pewnych powodów tylko wyjątki dziedziczone z
Exception
klasy bazowej są obsługiwane normalnie. Jako obejście, można ponownie podnieśćKeyboardInterrupt
jakoException
przykład:Zwykle otrzymasz następujący wynik:
Więc jeśli trafisz
^C
, otrzymasz:źródło
KeyboardInterrupt
a podczasmultiprocessing
wykonywania własnej wymiany danych IPC, totry..catch
nie zostanie aktywowany (oczywiście).raise KeyboardInterruptError
zreturn
. Musisz tylko upewnić się, że proces potomny zakończy się natychmiast po odebraniu KeyboardInterrupt. Zwracana wartość wydaje się być ignorowana, alemain
nadal otrzymywano KeyboardInterrupt.Zwykle ta prosta struktura działa w przypadku Ctrl- Cw puli:
Jak stwierdzono w kilku podobnych postach:
Przechwytuj przerwanie klawiatury w Pythonie bez try-except
źródło
Głosowana odpowiedź nie dotyczy podstawowego problemu, ale podobny efekt uboczny.
Jesse Noller, autor wieloprocesorowej biblioteki, wyjaśnia, jak poprawnie radzić sobie z CTRL + C
multiprocessing.Pool
w starym poście na blogu .źródło
os.setpgrp()
z przyszłościProcessPoolExecutor
nie obsługuje funkcji inicjatora. W systemie Unix można wykorzystać tęfork
strategię, wyłączając sighandler w głównym procesie przed utworzeniem puli, a następnie włączając go ponownie. W żwir , wyciszyćSIGINT
się na procesach potomnych domyślnie. Nie wiem, dlaczego nie robią tego samego z pulami Pythona. Na koniec użytkownik może ponownie ustawić przewodnikaSIGINT
na wypadek, gdyby chciał zrobić sobie krzywdę.Wydaje się, że istnieją dwie kwestie, które powodują, że wyjątki podczas przetwarzania wieloprocesowego są irytujące. Pierwszą (zauważoną przez Glenna) jest to, że aby uzyskać natychmiastową odpowiedź , musisz użyć
map_async
timeout zamiastmap
(tzn. Nie kończyć przetwarzania całej listy). Drugi (zauważony przez Andreya) jest taki, że przetwarzanie wieloprocesowe nie przechwytuje wyjątków, które nie dziedziczą poException
(npSystemExit
.). Oto moje rozwiązanie, które rozwiązuje oba te problemy:źródło
function
jest dość długi (setki sekund).map
i wszystko jest w porządku.@Linux Cli Aik
przedstawił poniżej rozwiązanie, które powoduje takie zachowanie. Używaniemap_async
nie zawsze jest pożądane, jeśli główny wątek zależy od wyników z procesów potomnych.Odkryłem, że na razie najlepszym rozwiązaniem jest nie korzystanie z funkcji multiprocessing.pool, ale raczej rozwijanie własnej funkcjonalności puli. Podałem przykład demonstrujący błąd z zastosowaniem apply_async, a także przykład pokazujący, jak w ogóle uniknąć korzystania z funkcji puli.
http://www.bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/
źródło
Jestem nowicjuszem w Pythonie. Szukałem odpowiedzi wszędzie i natknąłem się na ten i kilka innych blogów i filmów na youtube. Próbowałem skopiować, wkleić powyższy kod autora i odtworzyć go na moim Pythonie 2.7.13 w systemie Windows 7 64-bitowym. Jest blisko tego, co chcę osiągnąć.
Zmusiłem procesy podrzędne, aby ignorowały ControlC i powodowały zakończenie procesu nadrzędnego. Wygląda na to, że omijanie procesu potomnego pozwala mi uniknąć tego problemu.
Część rozpoczynająca się od
pool.terminate()
nigdy nie wydaje się być wykonywana.źródło
map_async
na użytkowniku, co mi się nie podoba. W wielu sytuacjach, takich jak moja, główny wątek musi poczekać na zakończenie poszczególnych procesów. To jeden z powodów, dla którychmap
istnieje!Możesz spróbować użyć metody apply_async obiektu Pool, na przykład:
Wynik:
Zaletą tej metody jest to, że wyniki przetworzone przed przerwaniem zostaną zwrócone w słowniku wyników:
źródło
O dziwo, wygląda na to, że musisz radzić sobie również
KeyboardInterrupt
z dziećmi. Spodziewałbym się, że będzie działać tak, jak napisano ... spróbuj zmienićslowly_square
na:To powinno działać zgodnie z oczekiwaniami.
źródło