Złap Ctrl + C / SIGINT i z wdziękiem Zakończ wieloprocesy w Pythonie
Jak złapać Ctrl + C w wieloprocesowym programie Pythona i zakończyć wszystkie procesy z wdziękiem, potrzebuję rozwiązania do pracy zarówno na unix i windows. Próbowałem:
import multiprocessing
import time
import signal
import sys
jobs = []
def worker():
signal.signal(signal.SIGINT, signal_handler)
while(True):
time.sleep(1.1234)
print "Working..."
def signal_handler(signal, frame):
print 'You pressed Ctrl+C!'
# for p in jobs:
# p.terminate()
sys.exit(0)
if __name__ == "__main__":
for i in range(50):
p = multiprocessing.Process(target=worker)
jobs.append(p)
p.start()
I to działa, ale nie sądzę, żeby to było właściwe rozwiązanie.
EDIT: To może być duplikat Ten
3 answers
Wcześniej przyjęte rozwiązanie mA warunki rasowe i nie działa z funkcjami map
i async
.
Poprawnym sposobem obsługi Ctrl + C / SIGINT
z multiprocessing.Pool
jest:
- aby proces ignorował
SIGINT
przed utworzeniem procesuPool
. W ten sposób utworzone procesy potomne dziedzicząSIGINT
handler. - Przywróć oryginalną obsługę
SIGINT
w procesie nadrzędnym po utworzeniuPool
. - Użyj
map_async
iapply_async
zamiast blokowaniamap
iapply
. - oczekiwanie na wyniki z timeoutem, ponieważ domyślne czekanie blokujące ignoruje wszystkie sygnały. To jest Python bug https://bugs.python.org/issue8296 .
Złożenie:
#!/bin/env python
from __future__ import print_function
import multiprocessing
import os
import signal
import time
def run_worker(delay):
print("In a worker process", os.getpid())
time.sleep(delay)
def main():
print("Initializng 2 workers")
original_sigint_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
pool = multiprocessing.Pool(2)
signal.signal(signal.SIGINT, original_sigint_handler)
try:
print("Starting 2 jobs of 5 seconds each")
res = pool.map_async(run_worker, [5, 5])
print("Waiting for results")
res.get(60) # Without the timeout this blocking call ignores all signals.
except KeyboardInterrupt:
print("Caught KeyboardInterrupt, terminating workers")
pool.terminate()
else:
print("Normal termination")
pool.close()
pool.join()
if __name__ == "__main__":
main()
Jak zauważył @YakovShklarov, istnieje okno czasu pomiędzy ignorowaniem sygnału a anulowaniem go w procesie macierzystym, podczas którego sygnał może zostać utracony. Użycie pthread_sigmask
zamiast tymczasowego zablokowania dostarczania sygnału w procesie macierzystym zapobiega utracie sygnału, jednak nie jest on dostępny w Pythonie-2.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-31 23:56:17
Rozwiązanie oparte jest na ten link i TEN link i rozwiązało problem, musiałem przenieść się naPool
chociaż:
import multiprocessing
import time
import signal
import sys
def init_worker():
signal.signal(signal.SIGINT, signal.SIG_IGN)
def worker():
while(True):
time.sleep(1.1234)
print "Working..."
if __name__ == "__main__":
pool = multiprocessing.Pool(50, init_worker)
try:
for i in range(50):
pool.apply_async(worker)
time.sleep(10)
pool.close()
pool.join()
except KeyboardInterrupt:
print "Caught KeyboardInterrupt, terminating workers"
pool.terminate()
pool.join()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 17:02:38
Po prostu obsłuż wyjątki od klawiatury-SystemExit w procesie roboczym:
def worker():
while(True):
try:
msg = self.msg_queue.get()
except (KeyboardInterrupt, SystemExit):
print("Exiting...")
break
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-04 14:59:16