Поймать Ctrl+C / SIGINT и выйти из многопроцессорных изящно в python
Как я могу поймать Ctrl+C в многопроцессорной программе python и выйти из всех процессов изящно, мне нужно решение для работы как на unix, так и на windows. Я пробовал следующее:
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 ответов:
ранее принятое решение есть условия гонки, и он не работает с
mapиasyncфункции.правильный способ обработки Ctrl+C/
SIGINTСmultiprocessing.Pool- это:
- сделать процесс игнорировать
SIGINTперед началом процесса это. Таким образом, созданные дочерние процессы наследуютSIGINTобработчик.- восстановить исходный
SIGINTобработчик в Родительском процессе послеPoolбыла создан.- использовать
map_asyncиapply_asyncвместо блокировкиmapиapply.- ждите результатов с таймаутом, потому что блокировка по умолчанию ожидает игнорирования всех сигналов. Это ошибка Pythonhttps://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()как отметил @YakovShklarov, существует окно времени между игнорированием сигнала и его отменой в Родительском процессе, во время которого сигнал может погибнуть. Используя
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()
просто обрабатывать KeyboardInterrupt-SystemExit исключения в рабочем процессе:
def worker(): while(True): try: msg = self.msg_queue.get() except (KeyboardInterrupt, SystemExit): print("Exiting...") break
Comments