Python: multitraitement.carte: Si un processus soulève une exception, pourquoi ne sont pas les autres processus " enfin des blocs?
Ma compréhension est que, finalement, les clauses doit *toujours* être exécuté si l'essai a été saisi.
import random
from multiprocessing import Pool
from time import sleep
def Process(x):
try:
print x
sleep(random.random())
raise Exception('Exception: ' + x)
finally:
print 'Finally: ' + x
Pool(3).map(Process, ['1','2','3'])
Résultat attendu est que pour chaque x qui est imprimé sur son propre par la ligne 8, il y doit être une occurrence de "Enfin x'.
Exemple de sortie:
$ python bug.py
1
2
3
Finally: 2
Traceback (most recent call last):
File "bug.py", line 14, in <module>
Pool(3).map(Process, ['1','2','3'])
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/pool.py", line 225, in map
return self.map_async(func, iterable, chunksize).get()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/pool.py", line 522, in get
raise self._value
Exception: Exception: 2
Il semble qu'une exception de terminaison d'un processus se termine, les parents et les frères et soeurs processus, même si il y a encore du travail nécessaire à faire dans d'autres processus.
Pourquoi ai-je tort? Pourquoi est-ce correct? Si cela est correct, comment devrait un en toute sécurité nettoyer les ressources en multi process Python?
Vous devez vous connecter pour publier un commentaire.
Réponse courte:
SIGTERM
atoutsfinally
.Réponse longue: Activer la journalisation avec
mp.log_to_stderr()
:La sortie d'enregistrement comprend:
Qui correspond à ce code dans
multiprocessing.pool._terminate_pool
:Chaque
p
danspool
est unmultiprocessing.Process
, et en appelantterminate
(au moins sur la non-machines Windows) appels SIGTERM:de
multiprocessing/forking.py
:Il descend donc à ce qui se passe quand un Python processus dans un
try
suite est envoyé unSIGTERM
.Considérons l'exemple suivant (test.py):
Si vous l'exécutez, puis de l'envoyer à un
SIGTERM
, puis le processus se termine immédiatement, sans entrer dans lefinally
suite, comme en témoigne pas de sortie, et aucun retard.Dans un terminal:
En deuxième terminal:
Résultat dans le premier terminal:
La comparer avec ce qui se passe lorsque le processus est envoyé un
SIGINT
(C-c
):En deuxième terminal:
Résultat dans le premier terminal:
Conclusion:
SIGTERM
atoutsfinally
.La réponse de unutbu certainement explique pourquoi vous obtenir le comportement que vous observez. Cependant, il convient de souligner que SIGTERM est envoyé seulement à cause de la façon dont
multiprocessing.pool._terminate_pool
est mis en œuvre. Si vous pouvez éviter d'utiliserPool
, alors vous pouvez obtenir le comportement que vous désirez. Voici une emprunté exemple:Après l'envoi d'un SIGINT est, exemple de sortie est:
Noter que le
finally
clause est couru pour tous les processus. Si vous avez besoin de la mémoire partagée, pensez à utiliserQueue
,Pipe
,Manager
, ou certains magasin externe commeredis
ousqlite3
.finally
re-soulève l'exception d'origine à moins que vousreturn
à partir de ce. L'exception est alors soulevée parPool.map
et tue toute votre application. Le sous-processus sont terminés et vous ne voyez pas d'autres exceptions.Vous pouvez ajouter un
return
à avaler l'exception:Alors vous devriez avoir
None
dans votremap
résultat lorsqu'une exception s'est produite.Pool.map
est modélisé d'aprèsmap
, qui s'arrête également lorsque la fonction mappé soulève une exception. Peut-être (essayer) de l'enfant, les processus sont terminés normalement et réellement faire exécuter lafinally
- seulement parce que votre thread principal est tué en premier la sortie de l'enfant n'est pas en lire plus.