J'essaie d'utiliser multiprocessing
la Pool.map()
fonction de pour répartir le travail simultanément. Lorsque j'utilise le code suivant, cela fonctionne bien:
import multiprocessing
def f(x):
return x*x
def go():
pool = multiprocessing.Pool(processes=4)
print pool.map(f, range(10))
if __name__== '__main__' :
go()
Cependant, lorsque je l'utilise dans une approche plus orientée objet, cela ne fonctionne pas. Le message d'erreur qu'il donne est:
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
__builtin__.instancemethod failed
Cela se produit lorsque ce qui suit est mon programme principal:
import someClass
if __name__== '__main__' :
sc = someClass.someClass()
sc.go()
et voici ma someClass
classe:
import multiprocessing
class someClass(object):
def __init__(self):
pass
def f(self, x):
return x*x
def go(self):
pool = multiprocessing.Pool(processes=4)
print pool.map(self.f, range(10))
Quelqu'un sait-il quel pourrait être le problème ou comment y remédier facilement?
python
multithreading
multiprocessing
pickle
pool
ventolin
la source
la source
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Réponses:
Le problème est que le multitraitement doit décaper les éléments pour les faire passer entre les processus, et les méthodes liées ne sont pas décapables. La solution de contournement (que vous considériez "facile" ou non ;-) consiste à ajouter l'infrastructure à votre programme pour permettre le décapage de telles méthodes, en l'enregistrant avec la méthode de bibliothèque standard copy_reg .
Par exemple, la contribution de Steven Bethard à ce fil (vers la fin du fil) montre une approche parfaitement réalisable pour permettre le picking / unpickling de méthode via
copy_reg
.la source
_pickle_method
retoursself._unpickle_method
, une méthode liée; donc bien sûr, le cornichon essaie maintenant de décapage QUE - et il fait comme vous l'avez dit: en appelant_pickle_method
, récursivement. C'est-à-dire qu'enOO
ingérant le code de cette manière, vous avez inévitablement introduit une récursion infinie. Je suggère de revenir au code de Steven (et de ne pas adorer l'autel d'OO lorsque cela n'est pas approprié: beaucoup de choses en Python sont mieux faites de manière plus fonctionnelle, et celle-ci en est une).Toutes ces solutions sont laides car le multitraitement et le décapage sont interrompus et limités à moins que vous ne sortiez de la bibliothèque standard.
Si vous utilisez un fork de
multiprocessing
calledpathos.multiprocesssing
, vous pouvez directement utiliser des classes et des méthodes de classe dans lesmap
fonctions de multiprocessing . En effet,dill
est utilisé à la place depickle
oucPickle
, etdill
peut sérialiser presque tout en python.pathos.multiprocessing
fournit également une fonction de carte asynchrone… et il peutmap
fonctionner avec plusieurs arguments (par exemplemap(math.pow, [1,2,3], [4,5,6])
)Voir: Que peuvent faire le multitraitement et l'aneth ensemble?
et: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
Et juste pour être explicite, vous pouvez faire exactement ce que vous vouliez faire en premier lieu, et vous pouvez le faire à partir de l'interprète, si vous le vouliez.
Obtenez le code ici: https://github.com/uqfoundation/pathos
la source
pathos
auteur. La version à laquelle vous faites référence a plusieurs années. Essayez la version sur github, vous pouvez utiliserpathos.pp
ou github.com/uqfoundation/ppft .pip install setuptools
, alorspip install git+https://github.com/uqfoundation/pathos.git@master
. Cela obtiendra les dépendances appropriées. Une nouvelle version est presque prête… maintenant presque toutpathos
fonctionne également sous Windows et est3.x
compatible.Vous pouvez également définir une
__call__()
méthode à l'intérieur de votresomeClass()
, qui appellesomeClass.go()
puis transmet une instance desomeClass()
au pool. Cet objet est picklable et il fonctionne très bien (pour moi) ...la source
__call__()
? Je pense que votre réponse pourrait être la plus propre - j'ai du mal à comprendre cette erreur, et la première fois que je viens voir l'appel. Soit dit en passant, cette réponse aide également à clarifier ce que fait le multitraitement: [ stackoverflow.com/a/20789937/305883]Quelques limitations cependant à la solution de Steven Bethard:
Lorsque vous enregistrez votre méthode de classe en tant que fonction, le destructeur de votre classe est appelé de façon surprenante chaque fois que le traitement de votre méthode est terminé. Donc, si vous avez 1 instance de votre classe qui appelle n fois sa méthode, les membres peuvent disparaître entre 2 exécutions et vous pouvez obtenir un message
malloc: *** error for object 0x...: pointer being freed was not allocated
(par exemple, ouvrir le fichier membre) oupure virtual method called, terminate called without an active exception
(ce qui signifie que la durée de vie d'un objet membre que j'ai utilisé était plus courte que ce que je pensais). J'ai obtenu ceci quand il s'agit de n supérieur à la taille de la piscine. Voici un petit exemple:Production:
le
__call__
méthode n'est pas si équivalente, car [Aucun, ...] est lu à partir des résultats:Donc, aucune des deux méthodes n'est satisfaisante ...
la source
None
revenez parce que votre définition de__call__
manque lereturn
: il devrait l'êtrereturn self.process_obj(i)
.Il existe un autre raccourci que vous pouvez utiliser, bien qu'il puisse être inefficace selon le contenu de vos instances de classe.
Comme tout le monde l’a dit, le problème est que le
multiprocessing
code doit décaper les choses qu'il envoie aux sous-processus qu'il a démarrés et que le sélecteur ne fait pas de méthodes d'instance.Cependant, au lieu d'envoyer la méthode d'instance, vous pouvez envoyer l'instance de classe réelle, plus le nom de la fonction à appeler, à une fonction ordinaire qui utilise ensuite
getattr
pour appeler la méthode d'instance, créant ainsi la méthode liée dans lePool
sous - processus. Cela revient à définir une__call__
méthode, sauf que vous pouvez appeler plusieurs fonctions membres.Voler le code @ EricH. De sa réponse et l'annoter un peu (je l'ai retapé d'où tous les changements de nom et ainsi de suite, pour une raison quelconque, cela semblait plus facile que le copier-coller :-)) pour illustrer toute la magie:
La sortie montre qu'en effet, le constructeur est appelé une fois (dans le pid d'origine) et le destructeur est appelé 9 fois (une fois pour chaque copie effectuée = 2 ou 3 fois par pool-worker-process selon les besoins, plus une fois dans l'original processus). C'est souvent OK, comme dans ce cas, car le sélecteur par défaut fait une copie de l'instance entière et (semi) la recopie secrètement - dans ce cas, en faisant:
- c'est pourquoi même si le destructeur est appelé huit fois dans les trois processus de travail, il compte de 1 à 0 à chaque fois - mais bien sûr, vous pouvez toujours avoir des ennuis de cette façon. Si nécessaire, vous pouvez fournir les vôtres
__setstate__
:dans ce cas par exemple.
la source
Vous pouvez également définir une
__call__()
méthode à l'intérieur de votresomeClass()
, qui appellesomeClass.go()
puis transmet une instance desomeClass()
au pool. Cet objet est picklable et il fonctionne très bien (pour moi) ...la source
La solution de parisjohn ci-dessus fonctionne très bien avec moi. De plus, le code semble propre et facile à comprendre. Dans mon cas, il y a quelques fonctions à appeler en utilisant Pool, j'ai donc modifié le code de parisjohn un peu plus bas. J'ai fait un appel pour pouvoir appeler plusieurs fonctions, et les noms des fonctions sont passés dans l'argument dict à partir de
go()
:la source
Une solution potentiellement triviale à cela est de passer à l'utilisation
multiprocessing.dummy
. Il s'agit d'une implémentation basée sur les threads de l'interface multitraitement qui ne semble pas avoir ce problème dans Python 2.7. Je n'ai pas beaucoup d'expérience ici, mais ce changement d'importation rapide m'a permis d'appeler apply_async sur une méthode de classe.Quelques bonnes ressources sur
multiprocessing.dummy
:https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.dummy
http://chriskiehl.com/article/parallelism-in-one-line/
la source
Dans ce cas simple, où
someClass.f
n'hérite pas de données de la classe et n'attache rien à la classe, une solution possible serait de les séparerf
, afin qu'elles puissent être décapées:la source
Pourquoi ne pas utiliser des fonctions séparées?
la source
J'ai rencontré ce même problème, mais j'ai découvert qu'il existe un encodeur JSON qui peut être utilisé pour déplacer ces objets entre les processus.
Utilisez ceci pour créer votre liste:
Ensuite, dans la fonction mappée, utilisez-la pour récupérer l'objet:
la source
Mise à jour: au jour de la rédaction de ce document, les nommésTuples sont sélectionnables (à partir de python 2.7)
Le problème ici est que les processus enfants ne peuvent pas importer la classe de l'objet -dans ce cas, la classe P-, dans le cas d'un projet multimodèle, la classe P doit être importable partout où le processus enfant est utilisé
une solution rapide consiste à le rendre importable en l'affectant aux globaux ()
la source