2010-09-08 2 views
26

У меня довольно сложный объект Python, который мне нужно разделить между несколькими процессами. Я запускаю эти процессы, используя multiprocessing.Process. Когда я делю объект с multiprocessing.Queue и multiprocessing.Pipe в нем, они разделяются просто отлично. Но когда я пытаюсь разделить объект с другими объектами не-мультипроцессорного модуля, кажется, что Python создает эти объекты. Это правда?Обмен сложным объектом между процессами Python?

Я пробовал использовать многопроцессорное обслуживание. Но я не уверен, какой тип должен быть? Мой класс объектов называется MyClass. Но когда я пытаюсь multiprocess.Value(MyClass, instance), он терпит неудачу с:

TypeError: this type has no size

Любая идея, что происходит?

+1

related: http://stackoverflow.com/questions/659865/python-multiprocessing-sharing-a-large-read-only-object-between-processes – tokland

ответ

23

Вы можете сделать это, используя классы Python Multiprocessing «Manager» и прокси-класс, который вы определяете. Из документов Python: http://docs.python.org/library/multiprocessing.html#proxy-objects

То, что вы хотите сделать, это определить прокси-класс для пользовательского объекта, а затем разделить объект с помощью «Remote Manager» - смотрите примеры в одной и той же странице, связанной док для «удаленный менеджер», где документы показывают, как делиться удаленной очередью. Вы будете делать то же самое, но ваш вызов your_manager_instance.register() будет включать ваш собственный прокси-класс в список аргументов.

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

+3

Код в этом вопросе помог дополнить страницу документа для меня. Это пример с пользовательским классом. http://stackoverflow.com/questions/11951750/sharing-object-class-instance-in-python-using-managers – EarlCrapstone

15

После много исследований и испытаний я обнаружил, что «Менеджер» выполняет эту работу в некоммерческом уровне.

Код, показанный ниже, показывает, что объект inst совместно используется между процессами, что означает, что свойство var10 из inst изменено снаружи, когда дочерний процесс меняет его.

from multiprocessing import Process, Manager 
from multiprocessing.managers import BaseManager 

class SimpleClass(object): 
    def __init__(self): 
     self.var = 0 

    def set(self, value): 
     self.var = value 

    def get(self): 
     return self.var 


def change_obj_value(obj): 
    obj.set(100) 


if __name__ == '__main__': 
    BaseManager.register('SimpleClass', SimpleClass) 
    manager = BaseManager() 
    manager.start() 
    inst = manager.SimpleClass() 

    p = Process(target=change_obj_value, args=[inst]) 
    p.start() 
    p.join() 

    print inst     # <__main__.SimpleClass object at 0x10cf82350> 
    print inst.get()    # 100 

Итак, выше код достаточно, если вам нужно только разделить простые объекты.

Почему нет комплекса? Поскольку может не сработать если объект вложен (объект внутри объекта):

from multiprocessing import Process, Manager 
from multiprocessing.managers import BaseManager 

class GetSetter(object): 
    def __init__(self): 
     self.var = None 

    def set(self, value): 
     self.var = value 

    def get(self): 
     return self.var 


class ChildClass(GetSetter): 
    pass 

class ParentClass(GetSetter): 
    def __init__(self): 
     self.child = ChildClass() 
     GetSetter.__init__(self) 

    def getChild(self): 
     return self.child 


def change_obj_value(obj): 
    obj.set(100) 
    obj.getChild().set(100) 


if __name__ == '__main__': 
    BaseManager.register('ParentClass', ParentClass) 
    manager = BaseManager() 
    manager.start() 
    inst2 = manager.ParentClass() 

    p2 = Process(target=change_obj_value, args=[inst2]) 
    p2.start() 
    p2.join() 

    print inst2     # <__main__.ParentClass object at 0x10cf82350> 
    print inst2.getChild()   # <__main__.ChildClass object at 0x10cf6dc50> 
    print inst2.get()    # 100 
    #good! 

    print inst2.getChild().get() # None 
    #bad! you need to register child class too but there's almost no way to do it 
    #even if you did register child class, you may get PicklingError :) 

Я думаю, что основная причина такого поведения в том, что Manager просто моноблок надстройки средств связи низкого уровня, таких как трубы /очередь.

Итак, этот подход не рекомендуется для многопроцессорного чехла. Это всегда лучше, если вы можете использовать низкоуровневые средства, как блокировки/семафора/трубы/очереди или высокоуровневые инструменты, такие как очереди Redis или Redis публикации/подписки для сложного случая использования (только моя рекомендация лол).

+0

Как поделиться сложным объектом? –

0

Чтобы сохранить некоторые головные боли с общими ресурсами, вы можете попытаться собрать данные, которые нуждаются в доступе к однопользовательскому ресурсу в операторе return функции, которая отображается, например.pool.imap_unordered, а затем дополнительно обработать в цикле, который извлекает частичные результаты:

for result in in pool.imap_unordered(process_function, iterable_data): 
    do_something(result) 

Если это не так много данных, которые получает возврат, то не может быть много накладных расходов в этом.

2

вот пакет python, который я сделал для этого (разделяя сложные объекты между процессами).

мерзавец: https://github.com/dRoje/pipe-proxy

Идея заключается в создании прокси для вашего объекта и передать его в процесс. Затем вы используете прокси-сервер, как будто у вас есть ссылка на исходный объект. Хотя вы можете использовать только вызовы методов, поэтому доступ к объектным переменным выполняется, отбрасывая сеттеры и геттеры.

Скажем, у нас есть объект, называемый «пример», создавая прокси и прокси-слушателю легко:

from pipeproxy import proxy 
example = Example() 
exampleProxy, exampleProxyListener = proxy.createProxy(example) 

Теперь вы посылаете прокси другому процессу.

p = Process(target=someMethod, args=(exampleProxy,)) p.start() 

Используйте его в другом процессе, как вы будете использовать исходный объект (пример):

def someMethod(exampleProxy): 
    ... 
    exampleProxy.originalExampleMethod() 
    ... 

Но вы должны слушать его в основном процессе:

exampleProxyListener.listen() 

Подробнее см. Здесь:

http://matkodjipalo.com/index.php/2017/11/12/proxy-solution-python-multiprocessing/

Смежные вопросы