Przykro mi, że nie mogę odtworzyć błędu na prostszym przykładzie, a mój kod jest zbyt skomplikowany, aby go opublikować. Jeśli uruchomię program w powłoce IPython zamiast zwykłego Pythona, wszystko zadziała dobrze.
Przejrzałem kilka wcześniejszych notatek na ten temat. Wszystkie zostały spowodowane przez użycie puli do wywołania funkcji zdefiniowanej w funkcji klasy. Ale tak nie jest w moim przypadku.
Exception in thread Thread-3:
Traceback (most recent call last):
File "/usr/lib64/python2.7/threading.py", line 552, in __bootstrap_inner
self.run()
File "/usr/lib64/python2.7/threading.py", line 505, in run
self.__target(*self.__args, **self.__kwargs)
File "/usr/lib64/python2.7/multiprocessing/pool.py", line 313, in _handle_tasks
put(task)
PicklingError: Can't pickle <type 'function'>: attribute lookup __builtin__.function failed
Byłbym wdzięczny za wszelką pomoc.
Aktualizacja : Funkcja I marynowana jest zdefiniowana na najwyższym poziomie modułu. Chociaż wywołuje funkcję, która zawiera funkcję zagnieżdżoną. tzn. f()
wywołuje g()
wywołania, h()
które mają funkcję zagnieżdżoną i()
, a ja dzwonię pool.apply_async(f)
. f()
, g()
, h()
Są określone na poziomie górnym. Próbowałem prostszego przykładu z tym wzorem i to działa.
dill
ipathos
. Jednak nie mam szczęścia do żadnego z rozwiązań podczas pracy z vtkobjects :( Ktoś zdążył uruchomić kod python w równoległym przetwarzaniu vtkPolyData?Odpowiedzi:
Oto lista tego, co można marynować . W szczególności funkcje można wybierać tylko wtedy, gdy są zdefiniowane na najwyższym poziomie modułu.
Ten fragment kodu:
zwraca błąd prawie identyczny z tym, który opublikowałeś:
Problem polega na tym, że
pool
wszystkie metody używają amp.SimpleQueue
do przekazywania zadań do procesów roboczych. Wszystko, co przechodzi,mp.SimpleQueue
musi być możliwe do odebrania ifoo.work
nie jest możliwe do odebrania, ponieważ nie jest zdefiniowane na najwyższym poziomie modułu.Można to naprawić, definiując funkcję na najwyższym poziomie, która wywołuje
foo.work()
:Zauważ, że
foo
jest do odebrania, ponieważFoo
jest zdefiniowany na najwyższym poziomie ifoo.__dict__
można go odebrać.źródło
pool = Pool()
Wiersz tutaj ). Nie spodziewałem się tego, i to może być powód, dla którego problem OP nadal występował.functool.partial
do funkcji najwyższego poziomu jest również możliwy do marynowania, nawet jeśli jest zdefiniowany w innej funkcji.Użyłbym
pathos.multiprocesssing
zamiastmultiprocessing
.pathos.multiprocessing
jest rozwidleniemmultiprocessing
tych zastosowańdill
.dill
potrafi serializować prawie wszystko w Pythonie, więc możesz wysłać o wiele więcej równolegle.pathos
Widelec posiada również możliwość pracy bezpośrednio z wieloma funkcjami argumentów, ile potrzeba do metod klasy.Pobierz
pathos
(i jeśli chceszdill
) tutaj: https://github.com/uqfoundationźródło
sudo pip install git+https://github.com/uqfoundation/dill.git@master
isudo pip install git+https://github.com/uqfoundation/pathos.git@master
sudo
(szczególnie ze źródeł zewnętrznych, takich jak github). Zamiast tego polecam uruchomić:pip install --user git+...
pip install pathos
nie działa niestety i daje następujący komunikat:Could not find a version that satisfies the requirement pp==1.5.7-pathos (from pathos)
pip install pathos
teraz działa ipathos
jest kompatybilny z Python 3.multiprocess
jest rozwidleniemmultiprocessing
gdziedill
zastąpiłpickle
w kilku miejscach w kodzie ... ale w zasadzie to wszystko.pathos
zapewnia dodatkowe warstwy API,multiprocess
a także ma dodatkowe zaplecza. Ale to jest sedno tego.Jak powiedzieli inni,
multiprocessing
mogą przenosić tylko obiekty Pythona do procesów roboczych, które mogą być marynowane. Jeśli nie możesz reorganizować kodu zgodnie z opisem unutbu, możesz skorzystać zdill
rozszerzonych funkcji wytrawiania / odznaczania do przesyłania danych (szczególnie danych kodu), jak pokazano poniżej.To rozwiązanie wymaga jedynie instalacji
dill
i żadnych innych bibliotek, ponieważpathos
:źródło
dill
ipathos
autorem… a jeśli masz rację, czy nie jest to o wiele ładniejsze, czystsze i bardziej elastyczne w użyciu,pathos
tak jak w mojej odpowiedzi? A może jestem trochę stronniczy…pathos
w momencie pisania i chciałem przedstawić rozwiązanie, które jest bardzo bliskie odpowiedzi. Teraz, gdy widziałem twoje rozwiązanie, zgadzam się, że to jest właściwa droga.Doh… I didn't even think of doing it like that.
To było trochę fajne.for
pętla. Normalnie widziałbym, jak równoległa procedura pobiera listę i zwraca listę bez pętli.Odkryłem, że mogę również wygenerować dokładnie ten błąd na doskonale działającym fragmencie kodu, próbując użyć na nim profilera.
Pamiętaj, że było to w systemie Windows (gdzie rozwidlenie jest nieco mniej eleganckie).
Biegłem:
I stwierdził, że usunięcie profilowania usunęło błąd, a umieszczenie profilowania przywróciło go. Doprowadzał mnie też do szału, ponieważ wiedziałem, że kod działa. Sprawdzałem, czy coś zaktualizowało pool.py ... a potem poczułem tonięcie i wyeliminowałem profilowanie i to wszystko.
Zamieszczam tutaj archiwum, na wypadek gdyby ktoś wpadł na nie.
źródło
pass
nie była „łatwa”.Kiedy pojawia się ten problem,
multiprocessing
prostym rozwiązaniem jest przejście zPool
naThreadPool
. Można to zrobić bez zmiany kodu oprócz importuDziała to, ponieważ ThreadPool dzieli pamięć z głównym wątkiem, zamiast tworzyć nowy proces - oznacza to, że wytrawianie nie jest wymagane.
Wadą tej metody jest to, że Python nie jest najlepszym językiem do obsługi wątków - używa czegoś o nazwie Global Interpreter Lock, aby zachować bezpieczeństwo wątków, co może spowolnić niektóre przypadki użycia tutaj. Jeśli jednak przede wszystkim wchodzisz w interakcję z innymi systemami (uruchamianie poleceń HTTP, rozmawianie z bazą danych, pisanie do systemów plików), kod prawdopodobnie nie jest związany z procesorem i nie odniesie większego trafienia. W rzeczywistości podczas pisania testów porównawczych HTTP / HTTPS stwierdziłem, że zastosowany tutaj model wątkowy ma mniejszy narzut i opóźnienia, ponieważ narzut związany z tworzeniem nowych procesów jest znacznie wyższy niż narzut związany z tworzeniem nowych wątków.
Więc jeśli przetwarzasz mnóstwo rzeczy w przestrzeni użytkownika Pythona, może to nie być najlepsza metoda.
źródło
Działa również w przypadku tablic numpy.
źródło
Ten błąd wystąpi również, jeśli w obiekcie modelu zostanie przekazana jakaś funkcja, która została przekazana do zadania asynchronicznego.
Upewnij się więc, że przekazane obiekty modelu nie mają wbudowanych funkcji. (W naszym przypadku używaliśmy
FieldTracker()
funkcji django-model-utils wewnątrz modelu do śledzenia określonego pola). Oto link do odpowiedniego problemu GitHub.źródło
Opierając się na rozwiązaniu @rocksportrocker, sensowne byłoby koperek podczas wysyłania i odzyskiwania wyników.
źródło