Korzystanie z puli wieloprocesorowej Pythona w terminalu i w modułach kodu dla Django lub Flask
Podczas korzystania z przetwarzania wieloprocesowego.Pool w Pythonie z poniższym kodem, istnieje pewne dziwne zachowanie.
from multiprocessing import Pool
p = Pool(3)
def f(x): return x
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
try: print(t.get(timeout=1))
except Exception: pass
Dostaję następujący błąd trzy razy (po jednym dla każdego wątku w Puli) i wyświetla "3" do "19":
AttributeError: 'module' object has no attribute 'f'
Pierwsze trzy wywołania apply_async nigdy nie zwracają.
Tymczasem, jeśli spróbuję:
from multiprocessing import Pool
p = Pool(3)
def f(x): print(x)
p.map(f, range(20))
Otrzymuję AttributeError 3 razy, powłoka drukuje "6" do "19", a następnie zawiesza się i nie można go zabić przez [Ctrl] + [C]
Multiprocessing docs mają do powiedzenia:
Co to znaczy?Funkcjonalność w tym pakiecie wymaga, aby moduł main był / align = "left" /
Dla wyjaśnienia, uruchamiam kod w Terminalu, aby przetestować funkcjonalność, ale ostatecznie chcę być w stanie umieścić to w modułach serwera www. Jak prawidłowo korzystać z przetwarzania wieloprocesorowego.Pool w terminalu Pythona i w modułach kodu?
3 answers
Oznacza to, że pule muszą być inicjowane po zdefiniowaniu funkcji, które mają być na nich uruchomione. Używanie pul w blokach if __name__ == "__main__":
działa, jeśli piszesz samodzielny skrypt, ale nie jest to możliwe ani w większych bazach kodu, ani w kodzie serwera(takim jak projekt Django czy Flask). Jeśli więc próbujesz użyć puli w jednym z nich, upewnij się, że postępujesz zgodnie z tymi wytycznymi, które są wyjaśnione w poniższych sekcjach:]}
- Inicjalizacja Pul u dołu modułów lub wewnątrz funkcje.
- nie wywołaj metod puli w globalnym zakresie modułu.
Alternatywnie, jeśli potrzebujesz tylko lepszej równoległości We / Wy (jak dostęp do baz danych lub połączenia sieciowe), możesz zaoszczędzić sobie całego tego bólu głowy i użyć puli wątków zamiast puli procesów. Dotyczy to całkowicie nieudokumentowanych:
from multiprocessing.pool import ThreadPool
Jego interfejs jest dokładnie taki sam jak w Pool, ale ponieważ używa wątków, a nie procesów, nie ma żadnych zastrzeżeń nie jest to jednak możliwe, ponieważ nie można uzyskać równoległości w wykonywaniu kodu, tylko równoległości w blokowaniu I/O.]}
Pule muszą być inicjalizowane po zdefiniowaniu funkcji, które mają być na nich uruchomione
Nierozpoznany tekst z dokumentów Pythona oznacza, że w momencie definiowania puli otaczający moduł jest importowany przez wątki w Puli. W przypadku terminala Pythona oznacza to cały i tylko kod, który uruchomiłeś więc daleko.
Zatem wszelkie funkcje, których chcesz użyć w puli, muszą zostać zdefiniowane przed zainicjalizowaniem puli. Dotyczy to zarówno kodu w module, jak i kodu w terminalu. Następujące modyfikacje kodu w pytaniu będą działać poprawnie:
from multiprocessing import Pool
def f(x): return x # FIRST
p = Pool(3) # SECOND
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
try: print(t.get(timeout=1))
except Exception: pass
Lub
from multiprocessing import Pool
def f(x): print(x) # FIRST
p = Pool(3) # SECOND
p.map(f, range(20))
Przez fine, mam na myśli fine na Unixie. Windows ma swoje własne problemy, w które nie Wchodzę.
Zastrzeżenia do korzystania z pul w modułach
Ale czekaj, jest więcej (do korzystania z basenów w moduły, które chcesz zaimportować gdzie indziej)!
Jeśli zdefiniujesz pulę wewnątrz funkcji, nie masz żadnych problemów. ale jeśli używasz obiektu Pool jako zmiennej globalnej w module, musi on być zdefiniowany na dole strony, a nie na górze. Chociaż jest to sprzeczne z najbardziej dobrym stylem kodu, jest to konieczne dla funkcjonalności. Sposobem korzystania z puli zadeklarowanej na górze strony jest używanie jej tylko z funkcjami importowanymi z innych modułów, takich jak więc:
from multiprocessing import Pool
from other_module import f
p = Pool(3)
p.map(f, range(20))
Importowanie wstępnie skonfigurowanej puli z innego modułu jest dość przerażające, ponieważ import musi nastąpić po tym, co chcesz na niej uruchomić, w ten sposób:
### module.py ###
from multiprocessing import Pool
POOL = Pool(5)
### module2.py ###
def f(x):
# Some function
from module import POOL
POOL.map(f, range(10))
I po drugie, jeśli uruchomisz cokolwiek na puli w globalnym zasięgu importowanego modułu, system zawiesi się . tzn. to nie działa:
### module.py ###
from multiprocessing import Pool
def f(x): return x
p = Pool(1)
print(p.map(f, range(5)))
### module2.py ###
import module
To jednak Działa , dopóki nic nie importuje module2:
### module.py ###
from multiprocessing import Pool
def f(x): return x
p = Pool(1)
def run_pool(): print(p.map(f, range(5)))
### module2.py ###
import module
module.run_pool()
Teraz, przyczyny tego są tylko bardziej dziwaczne i prawdopodobnie związane z powodem, że kod w pytaniu wypluwa błąd atrybutu Tylko raz, a potem wydaje się, że poprawnie wykonuje kod. Wygląda na to, że wątki puli (przynajmniej z pewną niezawodnością) przeładowują kod w module po wykonaniu. Fenomenalne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-22 16:41:54
Funkcja, którą chcesz wykonać na puli wątków, musi być już zdefiniowana podczas tworzenia puli.
To powinno zadziałać:
from multiprocessing import Pool
def f(x): print(x)
if __name__ == '__main__':
p = Pool(3)
p.map(f, range(20))
Powodem jest to ,że (przynajmniej na systemach posiadających fork
) Podczas tworzenia puli pracownicy są tworzone przez rozwidlenie bieżącego procesu. Więc jeśli funkcja docelowa nie jest jeszcze zdefiniowana w tym momencie, worker nie będzie mógł jej wywołać.
Na windows jest trochę inaczej, ponieważ windows nie ma fork
. Oto nowe procesy pracownicze uruchomiony i główny moduł jest importowany. Dlatego w systemie windows ważne jest, aby chronić wykonujący kod za pomocą if __name__ == '__main__'
. W przeciwnym razie każdy nowy worker ponownie wyeksponuje kod i tym samym odtwarza nowe procesy w nieskończoność, zawieszając program (lub system).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-21 20:37:42
Istnieje inne możliwe źródło tego błędu. Mam ten błąd podczas uruchamiania przykładowego kodu.
Źródło było takie, że pomimo poprawnego zainstalowania multiprocessingu, kompilator C++ nie został zainstalowany w moim systemie, o czym pip poinformował mnie podczas próby aktualizacji multiprocesingu. Warto więc sprawdzić, czy kompilator jest zainstalowany.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-30 14:06:18