Ich versuche zu verwenden , multiprocessing
‚s - Pool.map()
Funktion Arbeit gleichzeitig zu teilen aus. Wenn ich den folgenden Code verwende, funktioniert es einwandfrei:
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()
Wenn ich es jedoch objektorientierter verwende, funktioniert es nicht. Die Fehlermeldung lautet:
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
__builtin__.instancemethod failed
Dies tritt auf, wenn Folgendes mein Hauptprogramm ist:
import someClass
if __name__== '__main__' :
sc = someClass.someClass()
sc.go()
und das folgende ist meine someClass
Klasse:
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))
Weiß jemand, was das Problem sein könnte oder wie man es einfach umgehen kann?
python
multithreading
multiprocessing
pickle
pool
Ventolin
quelle
quelle
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Antworten:
Das Problem ist, dass bei der Mehrfachverarbeitung Dinge zwischen Prozessen verarbeitet werden müssen und gebundene Methoden nicht ausgewählt werden können. Die Problemumgehung (ob Sie es für "einfach" halten oder nicht ;-) besteht darin, die Infrastruktur zu Ihrem Programm hinzuzufügen, damit solche Methoden ausgewählt werden können, und sie bei der Standardbibliotheksmethode copy_reg zu registrieren .
Zum Beispiel zeigt Steven Bethards Beitrag zu diesem Thread (gegen Ende des Threads) einen perfekt praktikablen Ansatz, um das Beizen / Entpicken von Methoden über zu ermöglichen
copy_reg
.quelle
_pickle_method
Rückgabeself._unpickle_method
, eine gebundene Methode; Natürlich versucht pickle jetzt, DAS zu beizen - und es tut, was Sie gesagt haben: indem Sie_pickle_method
rekursiv anrufen . WennOO
Sie den Code auf diese Weise eingeben, haben Sie unweigerlich eine unendliche Rekursion eingeführt. Ich schlage vor, zu Stevens Code zurückzukehren (und nicht am Altar von OO anzubeten, wenn dies nicht angemessen ist: Viele Dinge in Python lassen sich am besten funktionaler erledigen, und dies ist eine).Alle diese Lösungen sind hässlich, da die Mehrfachverarbeitung und das Beizen unterbrochen und eingeschränkt sind, es sei denn, Sie springen außerhalb der Standardbibliothek.
Wenn Sie eine Verzweigung von
multiprocessing
aufgerufen verwendenpathos.multiprocesssing
, können Sie Klassen und Klassenmethoden direkt in denmap
Funktionen der Mehrfachverarbeitung verwenden. Dies liegt darandill
, dass anstelle vonpickle
oder verwendetcPickle
wird unddill
fast alles in Python serialisiert werden kann.pathos.multiprocessing
bietet auch eine asynchrone Zuordnungsfunktion… und kannmap
mit mehreren Argumenten funktionieren (zmap(math.pow, [1,2,3], [4,5,6])
)Siehe: Was können Multiprocessing und Dill zusammen tun?
und: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
Und um ganz klar zu sein, Sie können genau das tun, was Sie zuerst wollten, und Sie können es vom Dolmetscher aus tun, wenn Sie wollten.
Den Code erhalten Sie hier: https://github.com/uqfoundation/pathos
quelle
pathos
Autor. Die Version, auf die Sie sich beziehen, ist mehrere Jahre alt. Probieren Sie die Version auf github aus. Sie können sie verwendenpathos.pp
oder github.com/uqfoundation/ppft .pip install setuptools
dannpip install git+https://github.com/uqfoundation/pathos.git@master
. Dadurch werden die entsprechenden Abhängigkeiten erhalten. Eine neue Version ist fast fertig… jetztpathos
läuft fast alles auch unter Windows und ist3.x
kompatibel.Sie können auch eine
__call__()
Methode in Ihrem definierensomeClass()
, diesomeClass.go()
eine Instanz von aufruft und dannsomeClass()
an den Pool übergibt. Dieses Objekt ist pickleable und es funktioniert gut (für mich) ...quelle
__call__()
? Ich denke, Ihre Antwort könnte die sauberere sein - ich habe Mühe, diesen Fehler zu verstehen, und zum ersten Mal komme ich, um einen Anruf zu sehen. Übrigens hilft auch diese Antwort zu klären, was Multiprocessing macht: [ stackoverflow.com/a/20789937/305883]Einige Einschränkungen für Steven Bethards Lösung:
Wenn Sie Ihre Klassenmethode als Funktion registrieren, wird der Destruktor Ihrer Klasse überraschenderweise jedes Mal aufgerufen, wenn Ihre Methodenverarbeitung abgeschlossen ist. Wenn Sie also eine Instanz Ihrer Klasse haben, die n-mal ihre Methode aufruft, verschwinden Mitglieder möglicherweise zwischen zwei Läufen und Sie erhalten möglicherweise eine Nachricht
malloc: *** error for object 0x...: pointer being freed was not allocated
(z. B. geöffnete Mitgliedsdatei) oderpure virtual method called, terminate called without an active exception
(was bedeutet, dass die Lebensdauer eines von mir verwendeten Mitgliedsobjekts kürzer war als was ich dachte). Ich habe dies beim Umgang mit n größer als die Poolgröße bekommen. Hier ist ein kurzes Beispiel:Ausgabe:
Die
__call__
Methode ist nicht so äquivalent, weil [Keine, ...] aus den Ergebnissen gelesen werden:Keine der beiden Methoden ist also zufriedenstellend ...
quelle
None
zurück, weil Ihre Definition von__call__
fehltreturn
: es sollte seinreturn self.process_obj(i)
.Es gibt eine weitere Abkürzung, die Sie verwenden können, obwohl sie je nach den Instanzen in Ihrer Klasse ineffizient sein kann.
Wie jeder gesagt hat, besteht das Problem darin, dass der
multiprocessing
Code die Dinge auswählen muss, die er an die von ihm gestarteten Unterprozesse sendet, und der Pickler keine Instanzmethoden ausführt.Anstatt jedoch die Instanzmethode zu senden, können Sie die eigentliche Klasseninstanz sowie den Namen der aufzurufenden Funktion an eine normale Funktion senden, die dann
getattr
zum Aufrufen der Instanzmethode verwendet wird, wodurch die gebundene Methode imPool
Unterprozess erstellt wird. Dies ähnelt dem Definieren einer__call__
Methode, außer dass Sie mehr als eine Elementfunktion aufrufen können.Den Code von @ EricH. aus seiner Antwort zu stehlen und ihn ein wenig zu kommentieren (ich habe ihn neu getippt, daher alle Namensänderungen und so, aus irgendeinem Grund schien dies einfacher als Ausschneiden und Einfügen :-)), um all die Magie zu veranschaulichen:
Die Ausgabe zeigt, dass der Konstruktor tatsächlich einmal (in der ursprünglichen PID) und der Destruktor 9 Mal aufgerufen wird (einmal für jede erstellte Kopie = 2 oder 3 Mal pro Pool-Worker-Prozess nach Bedarf plus einmal im Original) Prozess). Dies ist wie in diesem Fall häufig in Ordnung, da der Standard-Pickler eine Kopie der gesamten Instanz erstellt und diese (halb-) heimlich neu auffüllt - in diesem Fall:
- Deshalb zählt der Destruktor, obwohl er in den drei Worker-Prozessen achtmal aufgerufen wird, jedes Mal von 1 auf 0 herunter -, aber natürlich können Sie auf diese Weise immer noch in Schwierigkeiten geraten. Bei Bedarf können Sie Ihre eigenen bereitstellen
__setstate__
:in diesem Fall zum Beispiel.
quelle
Sie können auch eine
__call__()
Methode in Ihrem definierensomeClass()
, diesomeClass.go()
eine Instanz von aufruft und dannsomeClass()
an den Pool übergibt. Dieses Objekt ist pickleable und es funktioniert gut (für mich) ...quelle
Die Lösung von Parisjohn oben funktioniert gut mit mir. Außerdem sieht der Code sauber und leicht verständlich aus. In meinem Fall gibt es einige Funktionen, die mit Pool aufgerufen werden können, daher habe ich den Code von Parisjohn etwas weiter unten geändert. Ich habe aufgerufen , um mehrere Funktionen aufrufen zu können, und die Funktionsnamen werden im Argument dict von übergeben
go()
:quelle
Eine möglicherweise triviale Lösung besteht darin, auf die Verwendung umzusteigen
multiprocessing.dummy
. Dies ist eine threadbasierte Implementierung der Multiprozessor-Schnittstelle, bei der dieses Problem in Python 2.7 nicht auftritt. Ich habe hier nicht viel Erfahrung, aber diese schnelle Importänderung ermöglichte es mir, apply_async für eine Klassenmethode aufzurufen.Ein paar gute Ressourcen zu
multiprocessing.dummy
:https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.dummy
http://chriskiehl.com/article/parallelism-in-one-line/
quelle
In diesem einfachen Fall, in dem
someClass.f
keine Daten von der Klasse geerbt und nichts an die Klasse angehängt werden, besteht eine mögliche Lösung darin, sie zu trennenf
, damit sie ausgewählt werden können:quelle
Warum nicht eine separate Funktion verwenden?
quelle
Ich bin auf dasselbe Problem gestoßen, habe jedoch festgestellt, dass es einen JSON-Encoder gibt, mit dem diese Objekte zwischen Prozessen verschoben werden können.
Verwenden Sie dies, um Ihre Liste zu erstellen:
Verwenden Sie dann in der zugeordneten Funktion Folgendes, um das Objekt wiederherzustellen:
quelle
Update: Ab dem Tag dieses Schreibens können NamedTuples ausgewählt werden (beginnend mit Python 2.7).
Das Problem hierbei ist, dass die untergeordneten Prozesse die Klasse des Objekts nicht importieren können - in diesem Fall die Klasse P -. Im Fall eines Projekts mit mehreren Modellen sollte die Klasse P überall dort importierbar sein, wo der untergeordnete Prozess verwendet wird
Eine schnelle Problemumgehung besteht darin, es importierbar zu machen, indem es auf Globals () angewendet wird.
quelle