Comment attraper un Ctrl + C en multiprocessus python et quitter tous les processus en douceur), j'ai besoin de la solution pour fonctionner à la fois sous Unix et Windows. J'ai essayé ce qui suit:
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 __== "__main__":
for i in range(50):
p = multiprocessing.Process(target=worker)
jobs.append(p)
p.start()
Et cela fonctionne bien, mais je ne pense pas que ce soit la bonne solution.
EDIT: Ceci pourrait être un doublon de celui-ci
La solution précédemment acceptée a des conditions de concurrence et ne fonctionne pas avec les fonctions map
et async
.
La bonne façon de gérer Ctrl + C/SIGINT
avec multiprocessing.Pool
est de:
SIGINT
avant qu'un processus Pool
ne soit créé. De cette façon, les processus enfants créés héritent du gestionnaire SIGINT
.SIGINT
d'origine dans le processus parent après la création d'un Pool
.map_async
et apply_async
au lieu de bloquer map
et apply
.Mettre ensemble:
#!/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 __== "__main__":
main()
Comme @YakovShklarov l'a noté, il existe un intervalle de temps entre ignorer le signal et le ne pas ignorer dans le processus parent, au cours duquel le signal peut être perdu. En utilisant pthread_sigmask
au lieu de bloquer temporairement la livraison du signal dans le processus parent empêcherait la perte du signal, cependant, il n'est pas disponible dans Python-2.
La solution est basée sur ce lien et ce lien et le problème a été résolu, je devais passer à Pool
bien que:
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 __== "__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()
Il vous suffit de gérer les exceptions KeyboardInterrupt-SystemExit dans votre processus de travail:
def worker():
while(True):
try:
msg = self.msg_queue.get()
except (KeyboardInterrupt, SystemExit):
print("Exiting...")
break