6

Je rencontre des problèmes pour implémenter un nouvel objet proxy defaultdict. Le documentation est un peu peur, donc je ne suis pas sûr de savoir comment s'y prendre correctement.Sous-classe multiprocessing.managers.BaseProxy

Je souhaite ajouter un defaultdict à la liste des types disponibles dans l'instance Manager. Vous ne pouvez pas utiliser la méthode Manager.register sur le stock multiprocessing.Manager donc je l'ai fait mon propre souche directeur de la multiprocessing.mangers.BaseManager

class Manager(BaseManager): 
    pass 

J'ai ensuite créé ma sous-classe de multiprocessing.managers.BaseProxy à la maison de la defaultdict (j'ai essayé d'abord d'avoir anthères stub qui serait sous-classe à la fois defaultdict et BaseProxy mais cela ne semble pas fonctionner Voici ce que j'ai actuellement:..

class ProxyDefaultDict(BaseProxy): 
    def __init__(self, default_factory=None, *args, **kwargs): 
     self.__data = defaultdict(default_factory) 
     super().__init__(*args, **kwargs) 

    def _callmethod(self, methodname, args=(), kwds={}): 
     return getattr(self.__data, methodname)(*args, **kwds) 

    def _getvalue(self): 
     return self.__data.copy() 

    def __repr__(self): 
     return self.__data.__repr__() 

    def __str__(self): 
     return self.__data.__str__() 

Manager.register('defaultdict', ProxyDefaultDict) 

L'objectif final est d'avoir un dictionnaire partagé qui partage en toute sécurité à travers les processus serrures à clé et fils Il re un exemple de la façon dont je l'image, il serait initialisés:

if __name__ == '__main__': 
    m = Manager() 
    d = m.defaultdict(m.Lock) 
    with d['named_lock']: 
     print('holding the lock') 

Cependant, je l'ai touché quelques problèmes:

  1. Une sous-classe de BaseManager semble être seulement initalizable par un gestionnaire de contexte à savoir with Manager() as m. Ce que je voudrais utiliser m = Manager() dans ce cas - que le multiprocessing.Manager permet. Pas la fin du monde mais plus curieux pourquoi c'est le cas et si c'est un signe que je fais quelque chose de mal.

  2. Le sous-classement multiprocessing.managers.BaseManager indique également que vous perdez les valeurs enregistrées par défaut de multiprocessing.Manager. Dans ce cas, je dois ré-enregistrer un ProxyLock pour mon manager (dont je ne suis pas sûr de la manière attendue de le faire). Est-il sûr de simplement sous-classer multiprocessing.Manager directement.

  3. Enfin, mon ProxyDefaultDict ne semble pas me permettre de remplacer proprement son __init__. Et je suis fatigué de ne pas appeler le BaseProxy.__init__ lors du sous-classement. Le problème est que BaseProxy accepte également les arguments positionnels. Je suppose que le moyen de contourner ceci est de faire de l'argument default_factory un argument clé uniquement, mais cela change l'interface attendue en defaultdict et me fait supposer que je fais quelque chose de mal ici encore. Les autres types comme Manager.Lock semblent être en mesure d'accepter des arguments positionnels.

Merci pour toute aide.

Répondre

3

Après avoir vu le code source, une petite modification de celui-ci fonctionne pour moi d'obtenir un proxy de type defaultdict sans problème (basé sur la façon dont le DictProxy intégré est créé).

from collections import defaultdict 

from multiprocessing.managers import MakeProxyType, SyncManager 

DefaultDictProxy = MakeProxyType("DefaultDictProxy", [ 
    '__contains__', '__delitem__', '__getitem__', '__len__', 
    '__setitem__', 'clear', 'copy', 'default_factory', 'fromkeys', 
    'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 
    'update', 'values']) 

SyncManager.register("defaultdict", defaultdict, DefaultDictProxy) 
# Can also create your own Manager here, just using built in for simplicity 

if __name__ == '__main__': 
    with SyncManager() as sm: 
     dd = sm.defaultdict(list) 
     print(dd['a']) 
     # [] 

Personnellement je trouve très pratique qu'en utilisant les outils déjà fournis, ne doivent même pas à se soucier de la façon de sous-classe vous-même. Cependant, je ne pense pas que cela vous permettra de créer le scénario de verrous par défaut que vous recherchez. Les verrous multitraitement sont conçus pour être hérités uniquement et, en général, les verrous ne peuvent pas être décapés, ce qui est requis pour les types de données transférés via les proxys.Exemple:

from multiprocessing import Lock 

    m = SyncManager() 
    m.start() 
    d = m.defaultdict(Lock) 
    print(d['named_lock']) 
    m.shutdown() 

soulèvera une erreur d'exécution:

RuntimeError: Lock objects should only be shared between processes through inheritance 
+0

Ah ouais, on dirait que vous pouvez appeler '' register' sur SyncManager' mais pas 'manager', merci. Je reçois une erreur différente lorsque j'essaie d'utiliser une serrure par défaut. Lorsque vous essayez d'utiliser un verrou géré, par exemple 'm.defaultdict (m.Lock)' Je reçois une erreur 'TypeError: Pickling un objet AuthenticationString est interdit pour des raisons de sécurité'. Avec un verrou non géré, j'obtiens un message Unserializable: ('#RETURN', ) '. En cours d'exécution sur Python 3.4. J'aurais pensé que si une serrure pouvait être peroxydée, elle pourrait être décapée. – freebie

+0

Oui, je pense que 3.6 a juste une erreur mise à jour/méthode de détection. Je reçois la même erreur si j'essaie d'utiliser des verrous de threading. Il existe d'autres idées https://stackoverflow.com/questions/17960296/trouble-using-a-lock-with-multiprocessing-pool-pickling-error pour contourner cela. – CasualDemon