Поймать Ctrl + C / SIGINT и вывести многопроцессы изящно в python

Как уловить Ctrl + C в многопроцессорной программе python и изящно выйти из всех процессов, мне нужно, чтобы решение работало как в unix, так и в окнах. Я пробовал следующее:

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() 

И это работает, но я не думаю, что это правильное решение.

EDIT: Это может быть дубликат этого

3 Solutions collect form web for “Поймать Ctrl + C / SIGINT и вывести многопроцессы изящно в python”

Ранее принятое решение имеет условия гонки и не работает с функциями map и async .

Правильный способ обработки Ctrl + C / SIGINT с multiprocessing.Pool :

  1. Заставьте процесс игнорировать SIGINT до создания Pool процессов. Таким образом, созданные дочерние процессы наследуют обработчик SIGINT .
  2. Восстановите исходный обработчик SIGINT в родительском процессе после создания Pool .
  3. Используйте map_async и apply_async вместо блокировки map и apply .
  4. Подождите по результатам с таймаутом, потому что блокировка по умолчанию игнорирует все сигналы. Это ошибка Python https://bugs.python.org/issue8296 .

Объединяя это:

 #!/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() 

Как отметил Ю.Яков Шкларов, есть окно времени между игнорированием сигнала и невыполнением его в родительском процессе, в течение которого сигнал может быть потерян. Использование pthread_sigmask вместо того, чтобы временно заблокировать доставку сигнала в родительском процессе, не позволит потерять сигнал, однако он недоступен в Python-2.

Решение основано на этой ссылке и этой ссылке, и она решила проблему, мне пришлось переместиться в Pool :

 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() в 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() 

Просто обрабатывайте исключения KeyboardInterrupt-SystemExit в рабочем процессе:

 def worker(): while(True): try: msg = self.msg_queue.get() except (KeyboardInterrupt, SystemExit): print("Exiting...") break в def worker(): while(True): try: msg = self.msg_queue.get() except (KeyboardInterrupt, SystemExit): print("Exiting...") break 
  • Python - Как сигналы отличаются от pubsub?
  • Django регистрирует пользователя IP для сигнала user_login_failed
  • Безопасно ли отключать слот до его вызова?
  • Django: сигнал, когда пользователь входит в систему?
  • Как остановить python от распространения сигналов на подпроцессы?
  • делать что-то при завершении программы python
  • CTRL + C не прерывает вызов в общую библиотеку с помощью CTYPES в Python
  • PySide / Qt: Слишком много аргументов для подключения сигнала к слоту?
  • Python - лучший язык программирования в мире.