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

Author: Community, 2012-07-03

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:

  1. aby proces ignorował SIGINT przed utworzeniem procesu Pool. W ten sposób utworzone procesy potomne dziedziczą SIGINT handler.
  2. Przywróć oryginalną obsługę SIGINT w procesie nadrzędnym po utworzeniu Pool.
  3. Użyj map_async i apply_async zamiast blokowania map i apply.
  4. 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.

 49
Author: Maxim Egorushkin,
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()
 34
Author: zenpoy,
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
 11
Author: derkan,
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