2009-09-24 6 views
1

Я пытаюсь использовать составную структуру через многопроцессорный менеджер, но я чувствовал себя в беде с «RuntimeError: максимальная глубина рекурсии превышена» при попытке использовать только один из классов Composite методы.диспетчер многопроцессорной обработки пифа и составное распределение шаблонов

Класс является токеном от code.activestate и проверен мной перед включением в диспетчер.

При извлечении класса в процессе и вызывает его AddChild() метод, который я держал RuntimeError, в то время как за пределами процесса он работает.

Составной класс наследуется от класса SpecialDict, который реализует метод ** ____ getattr() ____ ** .

Может быть возможно, что при вызове AddChild() интерпретатор питона ищет другой ** ____ GetAttr() ____ **, поскольку право один не проксированный менеджером?

Если так это не ясно мне правильный путь, чтобы сделать прокси-сервер для этого класса/метода

Следующий код воспроизводству именно это условие:

1) это 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) Я хочу использовать композит в этом 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) запустить все запуск с помощью 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() 

Посмотрите это related questions о том, как сделать прокси для CompositeDict() класса как предложено Альбертом.

Решение дается tgray работ, но не может избежать условий гонки

+0

ли вы изменить код в любом случае, когда вы взяли его из кода .activestate? Можете ли вы опубликовать соответствующий код, который возвращает класс в процесс, а затем вызывает метод addChild()? – tgray

+0

Код композита абсолютно не изменился. Я собираюсь обновить с помощью соответствующего кода .... – DrFalk3n

+1

Вы хотите только 2 подпроцесса? Когда я запускаю свой код, он продолжает запускать все больше и больше процессов, пока не сработает. – tgray

ответ

0

Python имеет глубину максимум рекурсии по умолчанию 1000 (или 999, не помню ...). Но вы можете изменить поведение по умолчанию таким образом:

import sys 
sys.setrecursionlimit(n) 

Где n это количество рекурсии, которые вы хотите разрешить.

Edit:

выше ответ не делает ничего, чтобы решить основную причину этой проблемы (как указано в комментариях). Его нужно использовать только в том случае, если вы намеренно рекурсируете более 1000 раз. Если вы находитесь в бесконечном цикле (например, в этой проблеме), вы, в конечном счете, нажмете все ограничения, которые вы установили.

Чтобы решить действительную проблему, я переписал ваш код с нуля, начиная так просто, как я мог бы сделать это, и построил его до того, что я считаю, что вы хотите:

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()) 

Я сделал это все в одном файле, но у вас не должно возникнуть проблем с его устранением.

Я добавил аргумент child_name для вашего потребителя, чтобы я мог проверить, что обновлялся CompositDict.

Обратите внимание, что есть и геттер , и сеттер для вашего CompositDict объекта. Когда у меня был только геттер, каждый потребитель переписывал CompositDict, когда он добавил ребенка.

Именно поэтому я также изменил свой зарегистрированный метод на get_shared вместо get_comp, так как вам нужен доступ к сеттеру, а также к получателю в вашем классе Consumer.

Кроме того, я не думаю, что вы хотите попробовать присоединиться к процессу своего менеджера, поскольку он будет «служить навсегда». Если вы посмотрите на источник для BaseManager (./Lib/multiprocessing/managers.py:Line 144), вы заметите, что функция serve_forever() помещает вас в бесконечный цикл, который разбивается только на KeyboardInterrupt или SystemExit.

Нижняя строка заключается в том, что этот код работает без рекурсивного цикла (насколько я могу судить), но дайте мне знать, если вы все еще испытываете свою ошибку.

+0

Да, но таким образом я не могу избежать проблемы и не аргументирую причину! – DrFalk3n

+0

Это не устраняет причину, даже в том случае, если это замаскирует проблему. – drAlberT

+0

Вы верны, я ответил слишком быстро, не понимая полной проблемы. – tgray

0

Возможно ли, что существует круговая ссылка между классами? Например, внешний класс имеет ссылку на составной класс, а составной класс ссылается на внешний класс.

Диспетчер многопроцессорности работает хорошо, но когда у вас большие сложные структуры классов, вы, вероятно, столкнетесь с ошибкой, когда тип/ссылка не может быть правильно сериализована. Другая проблема заключается в том, что ошибки в многопроцессорном менеджере очень загадочны. Это еще более усложняет условия отказа отладки.

+0

Быстрое совпадение с типом/ссылкой не правильно сериализовано и, да !, их отлаживать сложно. Это не похоже на круговую ссылку, как в вашем примере, или, по крайней мере, не столь очевидную. +1 за интерес по моему вопросу :-) – DrFalk3n

0

Я думаю, проблема заключается в том, что вы должны проинструктировать менеджера о том, как управлять вами объектом, который не является стандартным типом python.

В других мирах вы должны создать прокси-сервер для вас CompositeDict

Вы можете посмотреть на этот документ для примера: http://ruffus.googlecode.com/svn/trunk/doc/html/sharing_data_across_jobs_example.html