2009-09-24 3 views
1

Je tente de partager une structure composite à travers un gestionnaire multiprocesseur mais je me suis senti en difficulté avec un "RuntimeError: profondeur de récursivité maximale dépassé" en essayant d'utiliser un seul de la classe Composite méthodespython gestionnaire de multiprocessing et partage de modèles composites

La classe est symbolique de code.activestate et testé par moi avant d'être inclus dans le gestionnaire.

Lors de la récupération de la classe en un processus et invoquant son addChild() méthode j'ai gardé le RuntimeError, en dehors du processus cela fonctionne.

La classe composite hérite d'une classe SpecialDict, qui implémente une méthode ** ____ getattr() ____ ** .

Pourrait être possible que lors de l'appel addChild() l'interpréteur de python cherche un ** ____ getattr() ____ ** différent parce que le droit n'est pas mandaté par le gestionnaire?

Si oui Ce n'est pas clair pour moi la bonne façon de faire une procuration à cette classe/méthode

Le code suivant exactement cette condition reproduis:

1) c'est le manager.py:

from multiprocessing.managers import BaseManager 
from CompositeDict import * 

class PlantPurchaser(): 

    def __init__(self): 
     self.comp = CompositeDict('Comp') 

    def get_cp(self): 
     return self.comp 

class Manager(): 

    def __init__(self): 

     self.comp = QueuePurchaser().get_cp() 

     BaseManager.register('get_comp', callable=lambda:self.comp) 

     self.m = BaseManager(address=('127.0.0.1', 50000), authkey='abracadabra') 
     self.s = self.m.get_server() 

     self.s.serve_forever() 

2) Je veux utiliser le composite dans ce consumer.py:

from multiprocessing.managers import BaseManager 

class Consumer(): 

    def __init__(self): 

     BaseManager.register('get_comp') 

     self.m = BaseManager(address=('127.0.0.1', 50000), authkey='abracadabra') 
     self.m.connect() 

     self.comp = self.m.get_comp() 
     ret = self.comp.addChild('consumer') 

3) exécuter tout lancement par un controller.py:

from multiprocessing import Process 

class Controller(): 
    def __init__(self): 
     for child in _run_children(): 
      child.join() 

def _run_children(): 

    from manager import Manager 
    from consumer import Consumer as Consumer 

procs = (
     Process(target=Manager, name='Manager'), 
     Process(target=Consumer, name='Consumer'), 
     ) 

for proc in procs: 
    proc.daemon = 1 
    proc.start() 
return procs 

c = Controller() 

Jetez un oeil sur ce related questions comment faire une procuration pour la classe CompositeDict() comme suggéré par AlberT.

La solution donnée par tgray œuvres, mais ne peut pas éviter les conditions de course

+0

Avez-vous modifié le code de quelque manière que lorsque vous l'avez pris à partir du code .activestate? Pouvez-vous poster le code pertinent qui récupère la classe dans un processus puis appelle la méthode addChild()? – tgray

+0

Le code du composite est absolument inchangé. Je vais mettre à jour avec un code pertinent .... – DrFalk3n

+1

Voulez-vous seulement 2 sous-processus? Lorsque je lance votre code, il continue de lancer de plus en plus de processus jusqu'à ce qu'il échoue. – tgray

Répondre

0

Python a une profondeur maximale de récursion par défaut de 1000 (ou 999, j'oublier ...). Mais vous pouvez changer le comportement par défaut ainsi:

import sys 
sys.setrecursionlimit(n) 

n est le nombre de récurrences que vous souhaitez autoriser.

Edit:

La réponse ci-dessus ne fait rien pour résoudre la cause profonde de ce problème (comme l'a souligné dans les commentaires). Il doit seulement être utilisé si vous êtes intentionnellement récursif plus de 1000 fois. Si vous êtes dans une boucle infinie (comme dans ce problème), vous finirez par atteindre la limite que vous avez définie.

Pour répondre à votre problème réel, je récrit votre code de départ de zéro aussi simplement que je pouvais le faire et le construire jusqu'à ce que je crois est ce que vous voulez:

import sys 
from multiprocessing import Process 
from multiprocessing.managers import BaseManager 
from CompositDict import * 

class Shared(): 
    def __init__(self): 
     self.comp = CompositeDict('Comp') 

    def get_comp(self): 
     return self.comp 

    def set_comp(self, c): 
     self.comp = c 

class Manager(): 
    def __init__(self): 
     shared = Shared() 
     BaseManager.register('get_shared', callable=lambda:shared) 
     mgr = BaseManager(address=('127.0.0.1', 50000), authkey='abracadabra') 
     srv = mgr.get_server() 
     srv.serve_forever() 

class Consumer(): 
    def __init__(self, child_name): 
     BaseManager.register('get_shared') 
     mgr = BaseManager(address=('127.0.0.1', 50000), authkey='abracadabra') 
     mgr.connect() 

     shared = mgr.get_shared() 
     comp = shared.get_comp() 
     child = comp.addChild(child_name) 
     shared.set_comp(comp) 
     print comp 

class Controller(): 
    def __init__(self): 
     pass 

    def main(self): 
     m = Process(target=Manager, name='Manager') 
     m.daemon = True 
     m.start() 

     consumers = [] 
     for i in xrange(3): 
      p = Process(target=Consumer, name='Consumer', args=('Consumer_' + str(i),)) 
      p.daemon = True 
      consumers.append(p) 

     for c in consumers: 
      c.start() 
     for c in consumers: 
      c.join() 
     return 0 


if __name__ == '__main__': 
    con = Controller() 
    sys.exit(con.main()) 

Je l'ai fait tout dans un fichier, mais vous ne devriez pas avoir de problème pour le diviser.

J'ai ajouté un argument child_name à votre client afin que je puisse vérifier que le CompositDict était mis à jour.

Notez qu'il ya à la fois un getter et un setter pour votre objet CompositDict. Lorsque je n'avais qu'un getter, chaque consommateur écrasait le CompositDict lorsqu'il ajoutait un enfant.

C'est pourquoi j'ai également changé votre méthode enregistrée en get_shared au lieu de get_comp, car vous voudrez accéder au setter ainsi qu'au getter dans votre classe Consumer.

Aussi, je ne pense pas que vous voulez essayer de rejoindre votre processus de gestion, car il « servira pour toujours ». Si vous regardez, vous remarquerez que la fonction serve_forever() vous met dans une boucle infinie qui est seulement rompu par KeyboardInterrupt ou SystemExit la source pour la BaseManager (./Lib/multiprocessing/managers.py:Line 144).

Bottom line est que ce code fonctionne sans récursive en boucle (pour autant que je peux dire), mais laissez-moi savoir si vous rencontrez toujours votre erreur.

+0

Oui mais de cette façon je ne peux pas éviter le problème ni en discuter la cause! – DrFalk3n

+0

Cela ne pas éradiquer la cause, même dans le cas qui permettrait de masquer le problème – drAlberT

+0

Vous avez raison, j'ai répondu trop rapidement sans comprendre le problème complet. – tgray

0

Est-il possible qu'il y ait une référence circulaire entre les classes? Par exemple, la classe externe a une référence à la classe composite et la classe composite a une référence à la classe externe.

Le gestionnaire de multitraitement fonctionne bien, mais quand vous avez de grandes structures de classes compliquées, alors vous êtes susceptible de se heurter une erreur où un type/référence ne peut pas être sérialisé correctement. L'autre problème est que les erreurs du gestionnaire multi-traitement sont très énigmatiques. Cela rend les conditions d'échec de débogage encore plus difficiles.

+0

Il arrive assez vite de trébucher avec le type/référence non sérialisé correctement et, oui, il est difficile de les déboguer. Cela ne semble pas être une référence circulaire comme celle de votre exemple ou, du moins, pas si évidente. +1 pour l'intérêt sur ma question :-) – DrFalk3n

Questions connexes