En savoir plus sur Python Multi-traitement (à partir d'un article PMOTW )] et aimerions avoir des éclaircissements sur ce qu'est exactement la méthode join()
fait.
Dans un ancien tutoriel de 2008 , il est indiqué que sans l'appel p.join()
dans le code ci-dessous, "le processus enfant restera inactif et ne se terminera pas, devenant un zombie que vous devrez tuer manuellement" .
from multiprocessing import Process
def say_hello(name='world'):
print "Hello, %s" % name
p = Process(target=say_hello)
p.start()
p.join()
J'ai ajouté une impression du PID
ainsi qu'un time.sleep
À tester et, autant que je sache, le processus se termine de lui-même:
from multiprocessing import Process
import sys
import time
def say_hello(name='world'):
print "Hello, %s" % name
print 'Starting:', p.name, p.pid
sys.stdout.flush()
print 'Exiting :', p.name, p.pid
sys.stdout.flush()
time.sleep(20)
p = Process(target=say_hello)
p.start()
# no p.join()
dans les 20 secondes:
936 ttys000 0:00.05 /Library/Frameworks/Python.framework/Versions/2.7/Reso
938 ttys000 0:00.00 /Library/Frameworks/Python.framework/Versions/2.7/Reso
947 ttys001 0:00.13 -bash
après 20 secondes:
947 ttys001 0:00.13 -bash
Le comportement est le même avec p.join()
ajouté à la fin du fichier. Python propose une explication très lisible du module ; "Pour attendre qu’un processus ait terminé son travail, utilisez la méthode join (). ", mais il semble au moins qu'OS X le fasse de toute façon.
Je m'interroge également sur le nom de la méthode. La méthode .join()
concatène-t-elle quelque chose ici? Est-ce concaténer un processus avec sa fin? Ou partage-t-il simplement un nom avec la méthode native .join()
de Python?
La méthode join()
, lorsqu'elle est utilisée avec threading
ou multiprocessing
, n'est pas liée à str.join()
- elle ne concatène en réalité rien. Cela signifie simplement "attendez que ce [thread/processus] soit terminé". Le nom join
est utilisé parce que l'API du module multiprocessing
est similaire à l'API du module threading
, et le module threading
utilise join
pour son objet Thread
. Utiliser le terme join
pour signifier "attendre qu'un fil se termine" est courant dans de nombreux langages de programmation, donc Python l'a également adopté.
La raison pour laquelle vous voyez le délai de 20 secondes avec et sans l'appel de join()
est que, par défaut, lorsque le processus principal est prêt à quitter, il appelle implicitement join()
sur tous en cours d'exécution multiprocessing.Process
les instances. Ce n'est pas aussi clairement indiqué dans la documentation multiprocessing
que cela devrait être, mais cela est mentionné dans la section Instructions de programmation :
Rappelez-vous également que les processus non démoniques seront automatiquement joints.
Vous pouvez remplacer ce comportement en définissant l'indicateur daemon
sur Process
sur True
avant de démarrer le processus:
p = Process(target=say_hello)
p.daemon = True
p.start()
# Both parent and child will exit here, since the main process has completed.
Si vous faites cela, le processus enfant sera terminé dès que le processus principal sera terminé :
démon
Drapeau du démon du processus, une valeur booléenne. Ceci doit être défini avant que start () soit appelé.
La valeur initiale est héritée du processus de création.
Lorsqu'un processus se termine, il tente de mettre fin à tous ses processus enfants démoniaques.
Sans la join()
, le processus principal peut s'achever avant le processus enfant. Je ne sais pas dans quelles circonstances cela mène au zombie.
Le but principal de join()
est de s'assurer qu'un processus enfant est terminé avant que le processus principal ne fasse quoi que ce soit qui dépend du travail du processus enfant.
L'étymologie de join()
est le contraire de fork
, terme couramment utilisé dans les systèmes d'exploitation de la famille Unix pour créer des processus enfants. Un processus unique "divise" en plusieurs, puis "rejoint" en un.
Je ne vais pas expliquer en détail ce que join
fait, mais voici l'étymologie et l'intuition qui le sous-tend, ce qui devrait vous aider à vous en rappeler plus facilement le sens.
L'idée est que l'exécution " forks " dans plusieurs processus dont l'un est le maître, les autres ouvriers (ou "esclaves"). Lorsque les tâches sont terminées, elles "rejoignent" le maître afin de pouvoir reprendre l'exécution en série.
La méthode join
force le processus maître à attendre qu'un travailleur le rejoigne. La méthode aurait peut-être mieux s'appeler "wait", car c'est le comportement réel qu'elle cause dans le maître (et c'est ainsi qu'elle s'appelle dans POSIX, même si les threads POSIX l'appellent également "join"). La jonction ne survient que lorsque les threads coopèrent correctement. Ce n'est pas quelque chose que le maître fait .
Les noms "fork" et "join" ont été utilisés avec ce sens en multitraitement depuis 196 .
join()
est utilisé pour attendre la fin des processus de travail. Il faut appeler close()
ou terminate()
avant d'utiliser join()
.
Comme @Russell mentionné join est comme l'opposé de fork (qui génère des sous-processus).
Pour que la jointure soit exécutée, vous devez exécuter close()
, ce qui empêchera toute tâche supplémentaire d'être soumise au pool et sera quitté une fois toutes les tâches terminées. Sinon, exécuter terminate()
se terminera simplement en arrêtant immédiatement tous les processus de travail.
"the child process will sit idle and not terminate, becoming a zombie you must manually kill"
ceci est possible lorsque le processus principal (parent) se ferme mais que le processus enfant est toujours en cours d'exécution et qu'une fois terminé, il ne dispose d'aucun processus parent vers lequel retourner son statut de sortie.