Comment puis-je gérer les événements KeyboardInterrupt avec les pools de multitraitement de Python? Voici un exemple simple:
from multiprocessing import Pool
from time import sleep
from sys import exit
def slowly_square(i):
sleep(1)
return i*i
def go():
pool = Pool(8)
try:
results = pool.map(slowly_square, range(40))
except KeyboardInterrupt:
# **** THIS PART NEVER EXECUTES. ****
pool.terminate()
print "You cancelled the program!"
sys.exit(1)
print "\nFinally, here are the results: ", results
if __name__ == "__main__":
go()
Lors de l'exécution du code ci-dessus, le KeyboardInterrupt
est déclenché lorsque j'appuie sur ^C
, mais le processus se bloque simplement à ce stade et je dois le tuer en externe.
Je veux pouvoir appuyer ^C
à tout moment et faire en sorte que tous les processus se terminent gracieusement.
python
multiprocessing
pool
keyboardinterrupt
Fragsworth
la source
la source
Réponses:
Ceci est un bogue Python. Lors de l'attente d'une condition dans threading.Condition.wait (), KeyboardInterrupt n'est jamais envoyé. Repro:
L'exception KeyboardInterrupt ne sera pas délivrée tant que wait () ne sera pas retourné, et elle ne revient jamais, donc l'interruption ne se produit jamais. KeyboardInterrupt devrait presque certainement interrompre une condition d'attente.
Notez que cela ne se produit pas si un délai d'expiration est spécifié; cond.wait (1) recevra l'interruption immédiatement. Ainsi, une solution de contournement consiste à spécifier un délai d'expiration. Pour ce faire, remplacez
avec
ou similaire.
la source
D'après ce que j'ai trouvé récemment, la meilleure solution est de configurer les processus de travail pour ignorer complètement SIGINT et de confiner tout le code de nettoyage au processus parent. Cela résout le problème pour les processus de travail inactifs et occupés et ne nécessite aucun code de gestion des erreurs dans vos processus enfants.
Des explications et un exemple de code complet sont disponibles respectivement à l' adresse http://noswap.com/blog/python-multiprocessing-keyboardinterrupt/ et http://github.com/jreese/multiprocessing-keyboardinterrupt .
la source
time.sleep(10)
processus principal. Si vous supprimez ce sommeil, ou si vous attendez que le processus tente de se joindre au pool, ce que vous devez faire pour garantir que les travaux sont terminés, vous souffrez toujours du même problème qui est le processus principal ne ne recevez pas le KeyboardInterrupt pendant qu'il attend unejoin
opération d' interrogation .pool.terminate()
n'est jamais exécuté. Faire ignorer le signal aux enfants ne fait rien. La réponse de @ Glenn résout le problème..join()
que sur interruption - il vérifie simplement manuellement le résultat de l'.apply_async()
utilisationAsyncResult.ready()
pour voir s'il est prêt, ce qui signifie que nous avons terminé proprement.Pour certaines raisons, seules les exceptions héritées de la
Exception
classe de base sont gérées normalement. Pour contourner ce problème, vous pouvez re-déclencher votreKeyboardInterrupt
en tantException
qu'instance:Normalement, vous obtiendrez la sortie suivante:
Donc, si vous frappez
^C
, vous obtiendrez:la source
KeyboardInterrupt
est arrivé alors qu'ilmultiprocessing
effectue son propre échange de données IPC, letry..catch
ne sera pas activé (évidemment).raise KeyboardInterruptError
par un fichierreturn
. Vous devez simplement vous assurer que le processus enfant se termine dès que KeyboardInterrupt est reçu. La valeur de retour semble être ignorée,main
toujours le KeyboardInterrupt est reçu.Habituellement, cette structure simple fonctionne pour Ctrl- Csur Pool:
Comme indiqué dans quelques articles similaires:
Capturez l'interruption du clavier en Python sans essayer, sauf
la source
La réponse votée n'aborde pas le problème central mais un effet secondaire similaire.
Jesse Noller, l'auteur de la bibliothèque multitraitement, explique comment gérer correctement CTRL + C lors de l'utilisation
multiprocessing.Pool
dans un ancien article de blog .la source
os.setpgrp()
de l'intérieur du futurProcessPoolExecutor
ne prend pas en charge les fonctions d'initialisation. Sous Unix, vous pouvez tirer parti de lafork
stratégie en désactivant le sighandler sur le processus principal avant de créer le pool et en le réactivant par la suite. Dans Pebble , je mets en silenceSIGINT
les processus enfants par défaut. Je ne connais pas la raison pour laquelle ils ne font pas la même chose avec les pools Python. À la fin, l'utilisateur peut réinitialiser leSIGINT
gestionnaire au cas où il / elle voudrait se blesser.Il semble qu'il y ait deux problèmes qui font des exceptions tout en multitraitement ennuyeux. Le premier (noté par Glenn) est que vous devez utiliser
map_async
avec un timeout au lieu demap
pour obtenir une réponse immédiate (c'est-à-dire ne pas terminer le traitement de la liste entière). Le second (noté par Andrey) est que le multitraitement n'attrape pas les exceptions qui n'héritent pas deException
(par exemple,SystemExit
). Voici donc ma solution qui traite de ces deux éléments:la source
function
durée de vie est assez longue (des centaines de secondes).map
et tout va bien.@Linux Cli Aik
fourni une solution ci-dessous qui produit ce comportement. L'utilisationmap_async
n'est pas toujours souhaitée si le thread principal dépend des résultats des processus enfants.J'ai trouvé, pour le moment, que la meilleure solution était de ne pas utiliser la fonctionnalité multiprocessing.pool mais plutôt de lancer votre propre fonctionnalité de pool. J'ai fourni un exemple montrant l'erreur avec apply_async ainsi qu'un exemple montrant comment éviter d'utiliser complètement la fonctionnalité de pool.
http://www.bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/
la source
Je suis un novice en Python. Je cherchais partout une réponse et je suis tombé sur ceci et sur quelques autres blogs et vidéos YouTube. J'ai essayé de copier coller le code de l'auteur ci-dessus et de le reproduire sur mon python 2.7.13 sous Windows 7 64 bits. C'est proche de ce que je veux réaliser.
J'ai fait en sorte que mes processus enfants ignorent le ControlC et que le processus parent se termine. Il semble que le contournement du processus enfant évite ce problème pour moi.
La partie commençant à
pool.terminate()
semble ne jamais s'exécuter.la source
map_async
à l'utilisateur, ce que je n'aime pas particulièrement. Dans de nombreuses situations, comme la mienne, le thread principal doit attendre la fin des processus individuels. C'est l'une des raisons pour lesquellesmap
existe!Vous pouvez essayer d'utiliser la méthode apply_async d'un objet Pool, comme ceci:
Production:
Un avantage de cette méthode est que les résultats traités avant l'interruption seront renvoyés dans le dictionnaire des résultats:
la source
Curieusement, il semble que vous deviez également gérer
KeyboardInterrupt
les enfants. Je me serais attendu à ce que cela fonctionne comme écrit ... essayez de changerslowly_square
pour:Cela devrait fonctionner comme prévu.
la source