Les Interruptions de clavier avec python multitraitement Piscine
Comment puis-je gérer KeyboardInterrupt événements avec python multitraitement Piscines? 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
obtient augmenté lorsque j'appuie sur ^C
, mais le processus se bloque tout simplement à ce moment et je dois le tuer à l'extérieur.
Je veux être capable de presse ^C
à tout moment et tous les processus de fermer correctement.
J'ai résolu mon problème en utilisant psutil, vous pouvez voir la solution ici: stackoverflow.com/questions/32160054/...
OriginalL'auteur Fragsworth | 2009-09-10
Vous devez vous connecter pour publier un commentaire.
C'est un Python bug. Lors de l'attente d'une condition dans le filetage.Condition.wait(), KeyboardInterrupt n'est jamais envoyé. Repro:
L'exception KeyboardInterrupt ne sera livré qu'en attendre() retourne, et il ne revient jamais, de sorte que l'interruption n'arrive jamais. KeyboardInterrupt devraient presque certainement interrompre un état d'attente.
Noter que cela n'arrive pas si un délai est spécifié; cond.attendre(1) recevront l'interrompre immédiatement. Donc, une solution de contournement est de spécifier un délai. Pour ce faire, remplacer
avec
ou similaire.
Ce bogue a été déposé en tant que [Question 8296][1]. [1]: bugs.python.org/issue8296
Ce n'est pas vraiment arranger les choses. Parfois, je reçois le comportement attendu lorsque j'appuie sur Ctrl+C, d'autres fois non. Je ne sais pas pourquoi, mais il semble que peut-être La KeyboardInterrupt est reçu par l'un des processus au hasard, et je ne reçois que le comportement correct si le processus père est celui qui l'attrape.
autant que je sache, ce système permet explicitement à modifier d'autres postes à renforcer. Donc je ne vois pas vraiment le problème, permettez-moi de vous référer à: stackoverflow.com/tour
je viens d'ajouter le lien à la question que vous avez mentionné pour godssake! Et depuis que des modifications considérables devraient être pris la liberté d'ajouter quelques balises de mise en forme. Ce qui est bien dans les règles.
OriginalL'auteur Glenn Maynard
De ce que j'ai trouvé récemment, la meilleure solution est de mettre en place les processus de travail de l'ignorer SIGINT au total, et de confiner tous le code de nettoyage au processus parent. Cette solution résout le problème à la fois de repos et occupé les processus de travail, et ne nécessite pas de code de gestion d'erreur dans votre processus enfant.
Explication et exemple complet de code peut être trouvé à http://noswap.com/blog/python-multiprocessing-keyboardinterrupt/ et http://github.com/jreese/multiprocessing-keyboardinterrupt respectivement.
time.sleep(10)
dans le processus principal. Si vous deviez supprimer que le sommeil, ou si vous attendez jusqu'à ce que le processus tente de rejoindre sur la piscine, ce qui vous avez à faire afin de garantir les tâches sont terminées, vous avez encore souffrent du même problème qui est le processus principal ne reçoit pas le KeyboardInterrupt tout ce qu'il attend sur le sondagejoin
opération.Dans le cas où j'ai utilisé ce code dans la production, le temps.sleep() faisait partie d'une boucle, qui permettrait de contrôler l'état de chaque processus enfant, puis redémarrez certains processus sur un délai si nécessaire. Plutôt que de se joindre à (), qui allait les attendre sur tous les processus pour terminer, il serait de vérifier sur eux individuellement, en s'assurant que le processus maître est resté sensible.
Donc, il était plus occupé à attendre (peut-être avec la petite dort entre les vérifications), qui a interrogé pour l'achèvement du processus via une autre méthode plutôt que de se joindre? Si c'est le cas, il serait peut-être préférable d'inclure ce code dans votre blog, car vous pouvez garantir que tous les travailleurs ont terminé avant de tenter de la rejoindre.
Cela ne fonctionne pas. Seuls les enfants sont envoyés le signal. Le parent ne reçoit jamais, donc
pool.terminate()
n'est jamais exécutée. Avoir les enfants ignorer le signal ne sert à rien. @Glenn réponse résout le problème.Ma version de ce qui est dans le gist.github.com/admackin/003dd646e5fadee8b8d6 ; il ne demande pas
.join()
sauf sur l'interruption - il simplement manuellement vérifie le résultat de.apply_async()
à l'aide deAsyncResult.ready()
pour voir si il est prêt, ce qui signifie que nous avons proprement fini.OriginalL'auteur John Reese
Pour certaines raisons, les seules exceptions hérité de la base
Exception
classe sont traitées normalement. Comme solution de contournement, vous pouvez ré-augmenter votreKeyboardInterrupt
comme unException
exemple:Normalement vous devriez obtenir le résultat suivant:
Donc, si vous frappez
^C
, vous obtiendrez:KeyboardInterrupt
est arrivé alors quemultiprocessing
est l'exécution de ses propres CIB échange de données puis latry..catch
ne sera pas activé (évidemment).Cela a bien fonctionné pour moi.
Vous pouvez remplacer
raise KeyboardInterruptError
avec unreturn
. Vous avez juste à faire en sorte que le processus fils se termine dès que KeyboardInterrupt est reçu. La valeur de retour semble être ignoré, dansmain
encore la KeyboardInterrupt est reçu.OriginalL'auteur Andrey Vlasovskikh
Généralement cette structure simple qui fonctionne pour Ctrl-C sur la Piscine :
Comme l'a déjà mentionné quelques-uns des postes similaires:
Capture keyboardinterrupt en Python sans essayer-à l'exception de
OriginalL'auteur igco
Il semble qu'il y a deux questions qui font exceptions lors de multitraitement ennuyeux. Le premier (noté par Glenn), c'est que vous devez utiliser
map_async
avec un délai d'attente au lieu demap
afin d'obtenir une réponse immédiate (c'est à dire, ne pas terminer le traitement de l'ensemble de la liste). La deuxième (noté par Andrey), c'est que le multitraitement ne pas intercepter des exceptions qui n'héritent pas deException
(par exemple,SystemExit
). Voici donc ma solution qui traite à la fois de ces:Je n'ai pas remarqué de perte de performance, mais dans mon cas, le
function
est assez longue durée de vie (des centaines de secondes).Ce n'est pas vraiment le cas aujourd'hui, au moins de mes yeux et de l'expérience. Si vous attrapez le clavier exception dans le processus individuel de l'enfant et de l'attraper une fois de plus dans le processus principal, alors vous pouvez continuer à utiliser
map
et tout est bon.@Linux Cli Aik
fourni une solution ci-dessous qui produit ce comportement. À l'aide demap_async
n'est pas toujours désiré si le thread principal est dépendant sur les résultats du processus enfant.OriginalL'auteur Paul Price
L'voté réponse n'a pas à s'attaquer au cœur du problème similaire, mais un effet secondaire.
Jesse Noller, l'auteur de la multitraitement de la bibliothèque, explique comment traiter correctement avec CTRL+C lors de l'utilisation de
multiprocessing.Pool
dans un vieux post de blog.os.setpgrp()
de l'intérieur de l'avenirBien sûr, la seule différence est que
ProcessPoolExecutor
ne prend pas en charge l'initialiseur fonctions. Sur Unix, vous pourriez tirer parti de lafork
stratégie par la désactivation de la sighandler sur le processus principal avant la création de la Piscine et de le réactiver par la suite. Dans galets, j'silenceSIGINT
sur l'enfant, les processus par défaut. Je ne suis pas au courant de la raison pour laquelle ils ne font pas la même avec le Python Piscines. À la fin, l'utilisateur peut régler laSIGINT
gestionnaire dans le cas où il/elle veut du mal lui-même/elle-même.Cette solution semble empêcher Ctrl-C d'interrompre le processus principal.
Je viens de tester sur Python 3.5 et il fonctionne, quelle est la version de Python que vous utilisez? Quel OS?
OriginalL'auteur noxdafox
J'ai trouvé, pour le moment, la meilleure solution est de ne pas utiliser le multitraitement.piscine fonctionnalité, mais plutôt rouler votre propre piscine fonctionnalité. J'ai donné un exemple montrant que l'erreur avec apply_async, ainsi qu'un exemple montrant comment éviter l'utilisation de la piscine de la fonctionnalité tout à fait.
http://www.bryceboe.com/2010/08/26/python-multiprocessing-and-keyboardinterrupt/
Je n'ai pas remarqué de perte de performance de l'aide d'un délai d'attente, mais j'ai été en utilisant 9999 au lieu de 999999. La seule exception est lorsqu'une exception qui n'hérite pas de la classe Exception est soulevée: ensuite, vous devez attendre jusqu'à ce que le délai d'attente est atteint. La solution à cela est d'attraper toutes les exceptions (voir ma solution).
OriginalL'auteur bboe
Je suis un débutant en Python. Je cherchais partout pour répondre et de tomber sur cela, et quelques autres blogs et des vidéos youtube. J'ai essayé de copier coller l'auteur du code ci-dessus et de la reproduire sur mon python 2.7.13 sous windows 7 64 bits. Il est proche de ce que je veux atteindre.
J'ai fait mon enfant processus d'ignorer la ControlC et de rendre le processus parent résilier. Ressemble en contournant le processus de l'enfant ne d'éviter ce problème pour moi.
La partie commençant à
pool.terminate()
ne semble jamais à exécuter.map_async
sur l'utilisateur, que je n'aime pas particulièrement. Dans de nombreuses situations, comme la mienne, le thread principal doit attendre que le processus individuels à la fin. C'est une des raisons pour lesquellesmap
existe!OriginalL'auteur Linux Cli Aik
Vous pouvez essayer d'utiliser le apply_async méthode d'un objet Pool, comme ceci:
De sortie:
Un avantage de cette méthode est que les résultats traitées avant l'interruption sera renvoyé dans les résultats de dictionnaire:
OriginalL'auteur bparker856
Assez étrangement, il semble que vous avez à gérer la
KeyboardInterrupt
dans les enfants. Je me serais attendu à ce travail comme l'écrit... essayez de changerslowly_square
:Qui devrait fonctionner comme prévu.
c'est OK, mais vous risquez de perdre la trace des erreurs qui se produisent. retour de l'erreur avec une stacktrace pourrait fonctionner, de sorte que le processus parent peut dire qu'une erreur s'est produite, mais il continue à ne pas le quitter immédiatement lorsque l'erreur se produit.
OriginalL'auteur D.Shawley