Próbuję użyć multiprocessing
„s Pool.map()
funkcję podzielenie prac jednocześnie. Gdy używam następującego kodu, działa dobrze:
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()
Jednak gdy używam go w podejściu bardziej obiektowym, to nie działa. Wyświetlany komunikat o błędzie to:
PicklingError: Can't pickle <type 'instancemethod'>: attribute lookup
__builtin__.instancemethod failed
Dzieje się tak, gdy następujące są mój program główny:
import someClass
if __name__== '__main__' :
sc = someClass.someClass()
sc.go()
a oto moja someClass
klasa:
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))
Czy ktoś wie, na czym polega problem lub jak łatwo go obejść?
python
multithreading
multiprocessing
pickle
pool
ventolin
źródło
źródło
PicklingError: Can't pickle <class 'function'>: attribute lookup builtins.function failed
Odpowiedzi:
Problem polega na tym, że przetwarzanie wieloprocesorowe musi zalewać procesy między procesami, a powiązane metody nie są możliwe do wyboru. Obejściem problemu (czy uważasz to za „łatwe” czy nie ;-) jest dodanie infrastruktury do programu, aby umożliwić wytrawianie takich metod, rejestrując je za pomocą standardowej metody bibliotecznej copy_reg .
Na przykład wkład Stevena Betharda w ten wątek (pod koniec wątku) pokazuje jedno doskonale wykonalne podejście, które pozwala na wytrawianie / usuwanie zbędnych metod
copy_reg
.źródło
_pickle_method
zwrotyself._unpickle_method
, metoda związana; więc oczywiście marynata próbuje teraz marynować TO - i robi to tak, jak to kazałeś: dzwoniąc_pickle_method
rekurencyjnie. Tj.OO
Wprowadzając kod w ten sposób, nieuchronnie wprowadziłeś nieskończoną rekurencję. Sugeruję powrót do kodu Stevena (i nie oddawanie czci przy ołtarzu OO, gdy nie jest to właściwe: wiele rzeczy w Pythonie najlepiej zrobić w bardziej funkcjonalny sposób, i to jest jeden).Wszystkie te rozwiązania są brzydkie, ponieważ wieloprocesowość i wytrawianie są zepsute i ograniczone, chyba że wyjdziesz poza standardową bibliotekę.
Jeśli używasz rozwidlenia
multiprocessing
wywoływanychpathos.multiprocesssing
, możesz bezpośrednio używać klas i metod klasowych wmap
funkcjach wieloprocesowych . Jest tak, ponieważdill
jest używany zamiastpickle
lubcPickle
idill
może serializować prawie wszystko w Pythonie.pathos.multiprocessing
zapewnia także funkcję mapy asynchronicznej… i możemap
działać z wieloma argumentami (np.map(math.pow, [1,2,3], [4,5,6])
)Zobacz: Co wieloprocesowość i koperek mogą zrobić razem?
i: http://matthewrocklin.com/blog/work/2013/12/05/Parallelism-and-Serialization/
Mówiąc wprost, możesz zrobić dokładnie to, co chciałeś, i możesz to zrobić z tłumacza, jeśli chcesz.
Pobierz kod tutaj: https://github.com/uqfoundation/pathos
źródło
pathos
autorem Wersja, o której mowa, ma kilka lat. Wypróbuj wersję na github. Możesz użyćpathos.pp
lub github.com/uqfoundation/ppft .pip install setuptools
więcpip install git+https://github.com/uqfoundation/pathos.git@master
. Otrzymasz odpowiednie zależności. Nowa wersja jest już prawie gotowa… teraz prawie wszystkopathos
działa również w systemie Windows i jest3.x
kompatybilna.Możesz również zdefiniować
__call__()
metodę wewnątrz twojegosomeClass()
, która wywołuje,someClass.go()
a następnie przekazać instancjęsomeClass()
do puli. Ten obiekt jest zalewalny i działa dobrze (dla mnie) ...źródło
__call__()
? Myślę, że twoja odpowiedź może być czystsza - staram się zrozumieć ten błąd i po raz pierwszy przychodzę zobaczyć połączenie. Nawiasem mówiąc, także ta odpowiedź pomaga wyjaśnić, co robi wieloprocesowość: [ stackoverflow.com/a/20789937/305883]Niektóre ograniczenia rozwiązania Stevena Betharda:
Po zarejestrowaniu metody klasy jako funkcji, niszczyciel klasy jest zadziwiająco wywoływany za każdym razem, gdy przetwarzanie metody jest zakończone. Więc jeśli masz 1 instancję swojej klasy, która wywołuje n razy jej metodę, członkowie mogą zniknąć między 2 uruchomieniami i możesz otrzymać komunikat
malloc: *** error for object 0x...: pointer being freed was not allocated
(np. Otwarty plik członka) lubpure virtual method called, terminate called without an active exception
(co oznacza, że czas życia obiektu członka, którego użyłem, był krótszy niż co myślałem). Mam to, gdy mam do czynienia z n większą niż wielkość puli. Oto krótki przykład:Wynik:
__call__
Metoda nie jest więc równoważne, ponieważ [None, ...] są odczytywane z wynikami:Żadna z obu metod nie jest satysfakcjonująca ...
źródło
None
z powrotem, ponieważ definicja__call__
brakujereturn
: powinien byćreturn self.process_obj(i)
.Istnieje inny skrót, którego możesz użyć, chociaż może być nieefektywny w zależności od tego, co znajduje się w twojej klasie.
Jak wszyscy powiedzieli, problem polega na tym, że
multiprocessing
kod musi zalewać rzeczy, które wysyła do podprocesów, które uruchomił, a moduł wybierający nie stosuje metod instancji.Jednak zamiast wysyłać metodę instancji, możesz wysłać rzeczywistą instancję klasy oraz nazwę funkcji do wywołania, do zwykłej funkcji, która następnie używa
getattr
do wywołania metody instancji, tworząc w ten sposób powiązaną metodę wPool
podprocesie. Jest to podobne do definiowania__call__
metody, z tą różnicą, że można wywołać więcej niż jedną funkcję składową.Ukradłem kod @ EricH. Z jego odpowiedzi i trochę go opatrzyłem komentarzem (przepisałem go stąd wszystkie zmiany nazwy i takie, z jakiegoś powodu wydawało się to łatwiejsze niż wycinanie i wklejanie :-)) dla zilustrowania całej magii:
Dane wyjściowe pokazują, że rzeczywiście, konstruktor jest wywoływany raz (w oryginalnym pid), a destruktor jest wywoływany 9 razy (raz dla każdej wykonanej kopii = 2 lub 3 razy na proces-pulę-proces, zależnie od potrzeby, plus raz w oryginale proces). Jest to często OK, tak jak w tym przypadku, ponieważ domyślny moduł wybierający tworzy kopię całej instancji i (częściowo) potajemnie wypełnia ją ponownie - w tym przypadku wykonując:
- dlatego mimo że destruktor jest wywoływany osiem razy w trzech procesach roboczych, odlicza od 1 do 0 za każdym razem - ale oczywiście nadal możesz mieć kłopoty w ten sposób. W razie potrzeby możesz podać własne
__setstate__
:na przykład w tym przypadku.
źródło
Możesz również zdefiniować
__call__()
metodę wewnątrz twojegosomeClass()
, która wywołuje,someClass.go()
a następnie przekazać instancjęsomeClass()
do puli. Ten obiekt jest zalewalny i działa dobrze (dla mnie) ...źródło
Rozwiązanie z powyższego parisjohn działa dobrze ze mną. Ponadto kod wygląda na przejrzysty i łatwy do zrozumienia. W moim przypadku jest kilka funkcji, które można wywołać za pomocą Pool, więc zmodyfikowałem nieco kod parisjohn. Wykonałem wywołanie, aby móc wywołać kilka funkcji, a nazwy funkcji są przekazywane w argumencie dict z
go()
:źródło
Potencjalnie trywialnym rozwiązaniem tego problemu jest przejście na używanie
multiprocessing.dummy
. Jest to implementacja interfejsu wieloprocesowego oparta na wątkach, która nie wydaje się mieć tego problemu w Pythonie 2.7. Nie mam tutaj dużego doświadczenia, ale ta szybka zmiana importu pozwoliła mi wywołać metodę Apply_async w metodzie klasy.Kilka dobrych zasobów na
multiprocessing.dummy
:https://docs.python.org/2/library/multiprocessing.html#module-multiprocessing.dummy
http://chriskiehl.com/article/parallelism-in-one-line/
źródło
W tym prostym przypadku, w którym
someClass.f
nie dziedziczy się żadnych danych z klasy i nie dołącza niczego do klasy, możliwym rozwiązaniem byłoby oddzielenief
, aby można je było marynować:źródło
Dlaczego nie użyć osobnego func?
źródło
Natknąłem się na ten sam problem, ale odkryłem, że istnieje koder JSON, którego można używać do przenoszenia tych obiektów między procesami.
Użyj tego, aby utworzyć listę:
Następnie w funkcji mapowanej użyj tego do odzyskania obiektu:
źródło
Aktualizacja: na dzień pisania tego tekstu można pobrać nazwane krotki (począwszy od Pythona 2.7)
Problem w tym, że procesy potomne nie są w stanie zaimportować klasy obiektu - w tym przypadku klasy P - w przypadku projektu wielomodelowego klasa P powinna być importowana wszędzie tam, gdzie proces potomny zostanie wykorzystany
szybkim obejściem jest umożliwienie importu poprzez wpływanie na globals ()
źródło