Multiprocessing und der Pickle-Error

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
pyme
User
Beiträge: 7
Registriert: Dienstag 13. September 2011, 12:52

Hallo zusammen,
ich bin Python Neuling und versuche mich gerade an einem multiprozess basierten Programm. Leider bekomme ich das noch nicht so ganz hin, habe derzeit immer Pickle Errors. So wie ich das verstehe bekommt man die doch nur dann wenn man einen Datentyp verwendet der von Pickle nicht serialisiert werden kann. Aber meine Argumente bestehen aus einem Dictionary und einem String, welche ich auch ohne Probleme mit pickle.dump serialisieren kann.

Bin leider etwas ratlos, und weiß nich wo mein Problem liegt bzw. geschweige denn wie ich es behebe. Das Traceback sieht wie folgt aus:

Code: Alles auswählen

Traceback (most recent call last):
  File "D:\eclipse_workspace_peanuts\qwert\src\Gui\Aui\mPaneLeft.py", line 89, in start
    self.worker.start()
  File "C:\Python26\lib\multiprocessing\process.py", line 104, in start
    self._popen = Popen(self)
  File "C:\Python26\lib\multiprocessing\forking.py", line 239, in __init__
    dump(process_obj, to_child, HIGHEST_PROTOCOL)
  File "C:\Python26\lib\multiprocessing\forking.py", line 162, in dump
    ForkingPickler(file, protocol).dump(obj)
  File "C:\Python26\lib\pickle.py", line 224, in dump
    self.save(obj)
  File "C:\Python26\lib\pickle.py", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "C:\Python26\lib\pickle.py", line 419, in save_reduce
    save(state)
  File "C:\Python26\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python26\lib\pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "C:\Python26\lib\pickle.py", line 681, in _batch_setitems
    save(v)
  File "C:\Python26\lib\pickle.py", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "C:\Python26\lib\pickle.py", line 419, in save_reduce
    save(state)
  File "C:\Python26\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python26\lib\pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "C:\Python26\lib\pickle.py", line 681, in _batch_setitems
    save(v)
  File "C:\Python26\lib\pickle.py", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "C:\Python26\lib\pickle.py", line 419, in save_reduce
    save(state)
  File "C:\Python26\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python26\lib\pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "C:\Python26\lib\pickle.py", line 681, in _batch_setitems
    save(v)
  File "C:\Python26\lib\pickle.py", line 295, in save
    self.save_global(obj)
  File "C:\Python26\lib\pickle.py", line 748, in save_global
    (obj, module, name))
pickle.PicklingError: Can't pickle <class 'ctypes._FuncPtr'>: it's not found as ctypes._FuncPtr
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Python26\lib\multiprocessing\forking.py", line 342, in main
    self = load(from_parent)
  File "C:\Python26\lib\pickle.py", line 1370, in load
    return Unpickler(file).load()
  File "C:\Python26\lib\pickle.py", line 858, in load
    dispatch[key](self)
  File "C:\Python26\lib\pickle.py", line 880, in load_eof
    raise EOFError
EOFError
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Wieso der Fehler auftritt kann ich nicht sagen, allerdings ist das Problem bekannt, wenn du wxPython verwendest, mir ist leider auch kein Workaround bekannt
the more they change the more they stay the same
deets

Was ist denn in dem dictionary drin?
pyme
User
Beiträge: 7
Registriert: Dienstag 13. September 2011, 12:52

deets hat geschrieben:Was ist denn in dem dictionary drin?
Da sind nur Strings drin.
Dav1d
User
Beiträge: 1437
Registriert: Donnerstag 30. Juli 2009, 12:03
Kontaktdaten:

Im IRC hat sich meine Vermutung gerade bestätigt, er verwendet wxPython .. mir war es nicht möglich multiprocessing mit wxPython zum laufen zu kriegen, woran das aber genau lag ist mir entfallen.
the more they change the more they stay the same
pyme
User
Beiträge: 7
Registriert: Dienstag 13. September 2011, 12:52

Dav1d hat geschrieben:Im IRC hat sich meine Vermutung gerade bestätigt, er verwendet wxPython .. mir war es nicht möglich multiprocessing mit wxPython zum laufen zu kriegen, woran das aber genau lag ist mir entfallen.
Also falls dennoch jemand einen Ansatz für wxPython + multiprocessing hat. Er würde mich super glücklich machen.
deets

Debug doch mal da rein, was da genau passiert - welcher key da gepickelt wird. Machst du denn was mit ctypes?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Ist das jetzt eigentlich ein multiprocessing- oder ein pickle+multiprocessing-Problem? (Oder gar nur ein pickle-Problem!)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
deets

multiprocessing benutzt pickle um Argumente zu pickeln, die dann an remote prozesse uebergeben werden.
BlackJack

@pyme: Kann man denn Deinen `self.worker` picklen?
pyme
User
Beiträge: 7
Registriert: Dienstag 13. September 2011, 12:52

deets hat geschrieben:Debug doch mal da rein, was da genau passiert - welcher key da gepickelt wird. Machst du denn was mit ctypes?
Ja, in dem Worker-Objekt wird eine c-lib geladen und ctypes Objekte erstellt. Aber die Parameter die ich an das Objekt übergebe sind keine ctypes typen, die werden nur intern im Worker-Objekt erstellt und benutzt! Debuggen werde ich morgen früh und die Ergebnisse posten.
BlackJack hat geschrieben:@pyme: Kann man denn Deinen `self.worker` picklen?
Ist das denn notwendig? Dachte nur die Argumente werden gepickelt. Habe den Code gerade nicht hier, werde es gleich morgen früh probieren und Bescheid geben.
BlackJack

@pyme: Ja das ist unter Windows notwendig:
http://docs.python.org/library/multiprocessing.html#windows hat geschrieben:Also, if you subclass Process then make sure that instances will be picklable when the Process.start() method is called.
Und die `start()`-Methode ist ja laut Traceback genau das wo's kracht. Das Problem ist ja auch ein `ctypes`-Objekt.
pyme
User
Beiträge: 7
Registriert: Dienstag 13. September 2011, 12:52

BlackJack hat geschrieben:@pyme: Ja das ist unter Windows notwendig:
http://docs.python.org/library/multiprocessing.html#windows hat geschrieben:Also, if you subclass Process then make sure that instances will be picklable when the Process.start() method is called.
Und die `start()`-Methode ist ja laut Traceback genau das wo's kracht. Das Problem ist ja auch ein `ctypes`-Objekt.
Okay, Ursache gefunden :-). Immerhin einen kleinen Schritt weiter.

Ich bin jedes Objekt in meinem Sourcecode nacheinenander durchgegangen und habe es probiert in der Shell zu picklen. Beim picklen des ctypes.CDLL Objekts ist er dann schließlich mit der gleichen Picklefehlermeldung rausgesprungen, wie er sie beim Aufruf des process.start() bringt. Hier nochmal der Traceback:

Code: Alles auswählen

Traceback (most recent call last):
  File "<pyshell#54>", line 1, in <module>
    pickle.dumps(ctypes.CDLL(r"MeineDLL.dll"))
  File "C:\Python26\lib\pickle.py", line 1366, in dumps
    Pickler(file, protocol).dump(obj)
  File "C:\Python26\lib\pickle.py", line 224, in dump
    self.save(obj)
  File "C:\Python26\lib\pickle.py", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "C:\Python26\lib\pickle.py", line 419, in save_reduce
    save(state)
  File "C:\Python26\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python26\lib\pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "C:\Python26\lib\pickle.py", line 663, in _batch_setitems
    save(v)
  File "C:\Python26\lib\pickle.py", line 295, in save
    self.save_global(obj)
  File "C:\Python26\lib\pickle.py", line 748, in save_global
    (obj, module, name))
PicklingError: Can't pickle <class 'ctypes._FuncPtr'>: it's not found as ctypes._FuncPtr
So wie komm ich aber jetzt weiter? Muss ich dieses CDLL Objekt als shared Ctypes Object erstellen, habe dazu das hier in der Doku gefunden http://docs.python.org/library/multipro ... es-objects.

Danke für eure Hilfe bereits :-)
BlackJack

@pyme: Da Du wahrscheinlich nicht nur die DLL hast, sondern auch Funktionen daraus holst, also `ctypes`-Objekte hast die Zeiger auf Funktionen enthalten, wäre das vielleicht nur zufällig eine Lösung die funktioniert. Nämlich nur dann, wenn in dem anderen Prozess die gleichen Funktionen unter den gleichen Speicheradressen liegen. Ich weiss nicht ob das garantiert ist. Ich würde die DLL beziehungsweise die `ctypes`-Proxy-Objekte dafür nicht sharen, sondern in jedem Prozess neu laden/erstellen.

Mal ganz von `multiprocessing` abgesehen ist es IMHO sowieso „besser/schöner” eine DLL nicht in einem Objekt zu laden, sondern das in einem eigenen Modul zu machen. Denn man kann sowohl DLLs beziehungsweise Proxies dafür, als auch Module als Singletons ansehen.
pyme
User
Beiträge: 7
Registriert: Dienstag 13. September 2011, 12:52

BlackJack hat geschrieben:@pyme: Da Du wahrscheinlich nicht nur die DLL hast, sondern auch Funktionen daraus holst, also `ctypes`-Objekte hast die Zeiger auf Funktionen enthalten, wäre das vielleicht nur zufällig eine Lösung die funktioniert. Nämlich nur dann, wenn in dem anderen Prozess die gleichen Funktionen unter den gleichen Speicheradressen liegen. Ich weiss nicht ob das garantiert ist.
Ich dachte genau dafür benutzt man ein shared Memory. Um verschiedenen Prozessen einen gemeinsamen Addressbereich zu garantieren auf den alle zugreifen können.
BlackJack hat geschrieben:Ich würde die DLL beziehungsweise die `ctypes`-Proxy-Objekte dafür nicht sharen, sondern in jedem Prozess neu laden/erstellen.
Das fände ich auch die beste Methode, aber genau die verwende ich doch momentan erfolglos. Ich ruf ein Objekt, welches von der Klasse multiprocessing.process erbt auf. Damit sollte dieses Objekt als neuer Prozess gestartet werden. In diesem Objekt instanziiere ich die c-lib durch den aufruf ctypes.CDLL(). Aber leider kommt ja genau hier der PickleError von oben, wie soll ich das denn umgehen?
BlackJack hat geschrieben: Mal ganz von `multiprocessing` abgesehen ist es IMHO sowieso „besser/schöner” eine DLL nicht in einem Objekt zu laden, sondern das in einem eigenen Modul zu machen. Denn man kann sowohl DLLs beziehungsweise Proxies dafür, als auch Module als Singletons ansehen.
Aber würde das denn nicht genau dem widersprechen was ich vorhabe nämlich eine eigene Instanz für jeden Prozess zu erstellen? Oder habe ich da etwas falsch verstanden?
BlackJack

@pyme: In dem Shared Memory würdest Du ja nur die `ctypes`-Objekte ablegen, die als Proxy für die DLL dienen. Aber nicht die DLL selber. Die `ctypes`-Objekte enthalten Zeiger auf die Funktionen in der DLL. Diese Zeiger gelten aber nur für den Prozess in dem sie erstellt wurden. Du kannst den Zeiger zwar im Shared Memory ablegen, und ein anderer Prozess kann auf den Zeiger auch zugreifen, aber die Speicheradresse auf die er zeigt, kann in dem anderen Prozess eine völlig andere Bedeutung haben. Über Shared Memory kann man nur die Daten teilen, die auch tatsächlich innerhalb des Shared Memory liegen.

Das Objekt, das von `Process` erbt muss (zumindest unter Windows) komplett serialisierbar sein, weil das an den neu gestarteten Prozess übertragen wird. Und Zeiger, die auf etwas im Adressraum eines Prozesses zeigen, sind nicht serialisierbar, weil die Adresse auf die sie zeigen, nur innerhalb des laufenden Prozesses sinnvoll sind.

Prozesse haben, im Gegensatz zu Threads, jeweils einen eigenen Adressraum. Alle Exemplare von Python-Objekten in Prozessen sind neu erstellt und damit unterschiedlich. Das gilt selbst für die Shared Memory Objekte. Denn die *Python*-Objekte liegen alle im Adressraum des Prozesses und enthalten nur Zeiger auf Daten im Shared Memory.
pyme
User
Beiträge: 7
Registriert: Dienstag 13. September 2011, 12:52

Ich danke euch vielmals. Ich habe das Problem jetzt bewältigen können :-) es war tatsächlich dieses dämliche ctypes Objekt. Ich hatte leider die ganze Zeit einen Denkfehler. Ich dachte der Prozess wird bereits zu Beginn mit der Initialisierung der ProcessKlasse erzeugt. Da ich das ctypes Objekt in __init__ erzeugt habe, bin ich davon ausgegangen es wird bereits im neuen Prozess erzeugt. Jetzt hab ich den ctypes Aufruf in der run Methode und schon klappt alles :-)

Danke nochmal!
Antworten