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:

Funkcjonalność w tym pakiecie wymaga, aby moduł main był / align = "left" /

Co to znaczy?

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?

Author: maazza, 2013-09-22

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:]}

  1. Inicjalizacja Pul u dołu modułów lub wewnątrz funkcje.
  2. 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.

 35
Author: Zags,
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).

 4
Author: mata,
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.

 0
Author: ic_fl2,
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