2010-09-08 23 views
26

J'ai un objet Python assez complexe que j'ai besoin de partager entre plusieurs processus. Je lance ces processus en utilisant multiprocessing.Process. Lorsque je partage un objet avec multiprocessing.Queue et multiprocessing.Pipe, ils sont partagés très bien. Mais lorsque j'essaie de partager un objet avec d'autres objets de modules non multiprocesseurs, il semble que Python forge ces objets. Est-ce vrai?Partage d'un objet complexe entre des processus Python?

J'ai essayé d'utiliser multiprocessing.Value. Mais je ne suis pas sûr de ce que le type devrait être? Ma classe d'objets s'appelle MyClass. Mais quand j'essaie multiprocess.Value(MyClass, instance), il échoue avec:

TypeError: this type has no size

Toute idée de ce qui se passe?

+1

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

Répondre

23

Vous pouvez le faire en utilisant les classes «Manager» de multitraitement de Python et une classe de proxy que vous définissez. À partir des documents Python: http://docs.python.org/library/multiprocessing.html#proxy-objects

Ce que vous voulez faire est de définir une classe proxy pour votre objet personnalisé, puis de partager l'objet en utilisant un "Remote Manager" - regardez les exemples dans la même page doc liée pour "remote manager" où les docs montrent comment partager une file d'attente distante. Vous allez faire la même chose, mais votre appel à your_manager_instance.register() inclura votre classe de proxy personnalisée dans sa liste d'arguments. De cette manière, vous configurez un serveur pour partager l'objet personnalisé avec un proxy personnalisé. Vos clients ont besoin d'accéder au serveur (encore une fois, consultez les excellents exemples de configuration de configuration de l'accès client/serveur à une file d'attente distante, mais au lieu de partager une file d'attente, vous partagez l'accès à votre classe spécifique).

+3

Le code dans cette question a aidé à compléter la page de doc pour moi. C'est un exemple avec une classe personnalisée. http://stackoverflow.com/questions/11951750/sharing-object-class-instance-in-python-using-managers – EarlCrapstone

15

Après beaucoup de recherches et de tests, j'ai trouvé "Manager" faire ce travail dans un niveau d'objet non complexe.

Le code ci-dessous montre que l'objet inst est partagé entre les processus, ce qui signifie que la propriété var de inst est modifiée à l'extérieur lorsque le processus fils le modifie.

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 

Bon, le code ci-dessus est assez si vous avez besoin de partager des objets simples .

Pourquoi pas de complexe? Parce que il peut échouer si votre objet est imbriqué (objet à l'intérieur objet):

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

Je pense que la raison principale de ce comportement est dû au fait Manager est juste un candybar construire sur des outils de communication de bas niveau comme tuyau /queue. Par conséquent, cette approche est et non, ce qui est recommandé dans le cas du multitraitement. C'est toujours mieux si vous pouvez utiliser des outils de bas niveau comme lock/sémaphore/pipe/queue ou des outils de haut niveau comme Redis queue ou Redis publie/abonne pour un cas d'utilisation compliqué (seulement ma recommandation lol).

+0

Comment partager un objet complexe? –

0

Pour sauver des maux de tête avec des ressources partagées, vous pouvez essayer de collecter des données qui ont besoin d'accéder à une ressource singleton dans une déclaration de retour de la fonction qui est mappée par ex.pool.imap_unordered puis traiter plus dans une boucle qui récupère les résultats partiels:

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

Si ce n'est pas beaucoup de données qui est renvoyée, alors il n'y aurait pas de frais généraux à faire cela.

2

Voici un paquet python que j'ai fait juste pour ça (partager des objets complexes entre les processus).

git: https://github.com/dRoje/pipe-proxy

L'idée vous est de créer un proxy pour votre objet et passer à un processus. Ensuite, vous utilisez le proxy comme vous avez une référence à l'objet original. Bien que vous ne puissiez utiliser que des appels de méthode, l'accès aux variables d'objets est effectué par des setters et des getters.

Disons que nous avons un objet appelé « exemple », la création de proxy et de l'auditeur proxy est facile:

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

Maintenant vous envoyer le proxy à un autre processus.

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

Utilisez dans l'autre processus que vous utilisez l'objet d'origine (par exemple):

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

Mais vous ne devez l'écouter dans le processus principal:

exampleProxyListener.listen() 

En savoir plus et trouver des exemples ici:

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