2 votes

Le processus de multiprocessing Python appelle join par lui-même.

J'ai ce code :

class ExtendedProcess(multiprocessing.Process):
    def __init__(self):
        super(ExtendedProcess, self).__init__()
        self.stop_request = multiprocessing.Event()

    def join(self, timeout=None):
        logging.debug("stop request received")
        self.stop_request.set()
        super(ExtendedProcess, self).join(timeout)

    def run(self):
        logging.debug("process has started")
        while not self.stop_request.is_set():
            print "doing something"
        logging.debug("proc is stopping")

Lorsque j'appelle start() sur le processus, il devrait tourner indéfiniment, puisque self.stop_request() n'est pas activé. Après quelques milisecondes, join() est appelé par lui-même et interrompt l'exécution. Que se passe-t-il ? Pourquoi join est-il appelé par lui-même ?

De plus, lorsque je lance un débogueur et que je parcours ligne par ligne, tout fonctionne soudainement très bien..... Qu'est-ce que j'ai raté ?

OK, grâce à la réponse d'Ely, la raison m'a frappé :

Il y a une condition de course -

  1. nouveau processus créé...
  2. alors qu'il se lance et est sur le point de s'exécuter logging.debug("le processus a démarré") la fonction principale se termine.
  3. la fonction principale appelle sys exit et sur sys exit python appelle tous les processus terminés à se fermer avec join().
  4. puisque le processus n'a pas réellement frappé "while not self.stop_request.is_set()" join est appelé et "self.stop_request.set()". Maintenant stop_request.is_set et le code se ferme.

2voto

EMS Points 9249

Comme mentionné dans la question mise à jour, cela est dû à une condition de course. Ci-dessous, j'ai mis un premier exemple mettant en évidence une condition de course simpliste où la course est contre la sortie globale du programme, mais cela pourrait également être causé par d'autres types de sorties de portée ou d'autres conditions de course générales impliquant votre processus.

J'ai copié la définition de votre classe et ajouté du code "principal" pour l'exécuter, voici mon listing complet :

import logging
import multiprocessing
import time

class ExtendedProcess(multiprocessing.Process):
    def __init__(self):
        super(ExtendedProcess, self).__init__()
        self.stop_request = multiprocessing.Event()

    def join(self, timeout=None):
        logging.debug("stop request received")
        self.stop_request.set()
        super(ExtendedProcess, self).join(timeout)

    def run(self):
        logging.debug("process has started")
        while not self.stop_request.is_set():
            print("doing something")
            time.sleep(1)
        logging.debug("proc is stopping")

if __name__ == "__main__":
    p = ExtendedProcess()
    p.start()
    while True:
        pass

Le code ci-dessus fonctionne comme prévu avec Python 2.7.11 et 3.6.4. Il boucle à l'infini et le processus ne se termine jamais :

ely@eschaton:~/programming$ python extended_process.py 
doing something
doing something
doing something
doing something
doing something
... and so on

Cependant, si j'utilise plutôt ce code dans ma section principale, il se termine immédiatement (comme prévu) :

if __name__ == "__main__":
    p = ExtendedProcess()
    p.start()

Cette sortie est due au fait que l'interpréteur atteint la fin du programme, ce qui déclenche la destruction automatique de la fonction p lorsqu'il sort de la portée de l'ensemble du programme.

Notez que cela pourrait également expliquer pourquoi cela fonctionne pour vous dans le débogueur. Il s'agit d'une session de programmation interactive, donc après que vous ayez lancé p Il ne serait pas automatiquement détruit, à moins que vous ne l'invoquiez d'une manière ou d'une autre à l'intérieur d'une portée qui est quittée pendant que vous vous déplacez dans le débogueur.

Pour vérifier le comportement de la jointure, j'ai également essayé avec ce bloc principal :

if __name__ == "__main__":
    log = logging.getLogger()
    log.setLevel(logging.DEBUG)
    p = ExtendedProcess()
    p.start()
    st_time = time.time()
    while time.time() - st_time < 5:
        pass
    p.join()
    print("Finished!")

et cela fonctionne comme prévu :

ely@eschaton:~/programming$ python extended_process.py 
DEBUG:root:process has started
doing something
doing something
doing something
doing something
doing something
DEBUG:root:stop request received
DEBUG:root:proc is stopping
Finished!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X