multiprocessing.Pool: Kiedy stosować zastosować, zastosować_async lub mapę?

Odpowiedzi:

424

W dawnych czasach Pythona, aby wywołać funkcję z dowolnymi argumentami, użyłbyś apply:

apply(f,args,kwargs)

applynadal istnieje w Python2.7, ale nie w Python3 i generalnie nie jest już używany. Dzisiaj,

f(*args,**kwargs)

jest preferowany. Te multiprocessing.Poolmoduły stara się zapewnić podobny interfejs.

Pool.applyjest jak Python apply, z tym wyjątkiem, że wywołanie funkcji jest wykonywane w osobnym procesie. Pool.applyblokuje do zakończenia funkcji.

Pool.apply_asyncjest również podobny do wbudowanego w Pythona apply, z tym wyjątkiem, że wywołanie natychmiast wraca zamiast czekać na wynik. AsyncResultZwracany jest obiekt. Wywołujesz jego get()metodę, aby pobrać wynik wywołania funkcji. Te get()metody bloki aż funkcja jest zakończona. Jest zatem pool.apply(func, args, kwargs)równoważny z pool.apply_async(func, args, kwargs).get().

W przeciwieństwie do Pool.applyThe Pool.apply_asyncmetoda ma również wywołania zwrotnego, która, jeśli dostarczone, jest wywoływana, gdy funkcja jest kompletna. Można tego użyć zamiast dzwonić get().

Na przykład:

import multiprocessing as mp
import time

def foo_pool(x):
    time.sleep(2)
    return x*x

result_list = []
def log_result(result):
    # This is called whenever foo_pool(i) returns a result.
    # result_list is modified only by the main process, not the pool workers.
    result_list.append(result)

def apply_async_with_callback():
    pool = mp.Pool()
    for i in range(10):
        pool.apply_async(foo_pool, args = (i, ), callback = log_result)
    pool.close()
    pool.join()
    print(result_list)

if __name__ == '__main__':
    apply_async_with_callback()

może dać wynik taki jak

[1, 0, 4, 9, 25, 16, 49, 36, 81, 64]

Zwróć uwagę, w przeciwieństwie pool.mapdo kolejności wyników, może nie odpowiadać kolejności, w której pool.apply_asyncpołączenia zostały wykonane.


Jeśli więc chcesz uruchomić funkcję w osobnym procesie, ale chcesz, aby bieżący proces był blokowany, dopóki funkcja nie powróci, użyj Pool.apply. Jak Pool.apply, Pool.mapbloki aż do całkowitego wyniku jest zwracana.

Jeśli chcesz, aby pula procesów roboczych wykonywała wiele wywołań funkcji asynchronicznie, użyj Pool.apply_async. Kolejność wyników nie jest gwarantowana być taka sama jak kolejność wywołań Pool.apply_async.

Zauważ też, że możesz wywołać wiele różnych funkcji za pomocą Pool.apply_async(nie wszystkie połączenia muszą korzystać z tej samej funkcji).

Natomiast Pool.mapstosuje tę samą funkcję do wielu argumentów. Jednak w przeciwieństwie Pool.apply_asyncdo wyników zwracane są w kolejności odpowiadającej kolejności argumentów.

unutbu
źródło
11
Czy powinno być if __name__=="__main__"wcześniej apply_async_with_callback()w systemie Windows?
jfs
3
Wielkie dzięki. co powiesz na map_async?
Phyo Arkar Lwin
38
Zajrzyj do multiprocessing / pool.py, a zobaczysz, że Pool.map(func,iterable)jest to równoważne z Pool.map_async(func,iterable).get(). Tak więc związek pomiędzy Pool.mapi Pool.map_asyncjest podobny do tego z Pool.applyi Pool.apply_async. Te asyncpolecenia wrócić natychmiast, nieposiadający asyncpolecenia blokowania. Te asyncpolecenia mają też zwrotnego.
unutbu
7
Decydowanie między użyciem Pool.mapi Pool.applyjest podobne do decydowania, kiedy użyć maplub applyw Pythonie. Wystarczy użyć narzędzia, które pasuje do pracy. Decyzja między użyciem wersji asynca asyncwersją inną zależy od tego, czy chcesz, aby wywołanie blokowało bieżący proces i / lub czy chcesz użyć wywołania zwrotnego.
unutbu
6
@falsePockets: Tak. Każde wywołanie apply_asynczwraca ApplyResultobiekt. Wywołanie że ApplyResult„s getmetoda zwróci wartość zwracaną Associated funkcja (lub podbicie mp.TimeoutErrorjeśli czasy-out połączenia). Więc jeśli umieścić ApplyResults w uporządkowanej listy, a następnie wywołanie ich getmetody zwróci wyniki w tej samej kolejności. Możesz jednak użyć pool.mapw tej sytuacji.
unutbu
75

W odniesieniu applydo map:

pool.apply(f, args): fjest wykonywany tylko w JEDNEJ z pracowników puli. Uruchomi się JEDEN z procesów w puli f(args).

pool.map(f, iterable): Ta metoda dzieli iterowalny fragment na kilka części, które przekazuje do puli procesów jako osobne zadania. Wykorzystujesz więc wszystkie procesy w puli.

kakhkAtion
źródło
4
co jeśli iterowalny jest generator
RustyShackleford
Hmm ... Dobre pytanie. Szczerze mówiąc, nigdy nie korzystałem z pul z generatorami, ale ten wątek może być pomocny: stackoverflow.com/questions/5318936/...
kakhkAtion
@kakhkAtion Jeśli chodzi o zastosowanie, jeśli tylko jeden z pracowników wykonuje tę funkcję, co robią reszta pracowników? Czy muszę dzwonić wielokrotnie, aby reszta pracowników wykonała zadanie?
Moondra
3
Prawdziwe. Spójrz także na pool.apply_async, jeśli chcesz asynchronicznie spożywać pracowników. „pool_apply blokuje, dopóki wynik nie będzie gotowy, więc apply_async () lepiej nadaje się do równoległego wykonywania pracy”
kakhkAtion
1
Co się stanie, gdy mam 4 procesy, ale zadzwoniłem apply_async()8 razy? Czy automatycznie obsłuży to w kolejce?
Saravanabalagi Ramachandran
31

Oto przegląd w formie tabeli w celu pokazania różnic między Pool.apply, Pool.apply_async, Pool.mapi Pool.map_async. Wybierając jeden, musisz wziąć pod uwagę wiele argumentów, współbieżność, blokowanie i porządkowanie:

                  | Multi-args   Concurrence    Blocking     Ordered-results
---------------------------------------------------------------------
Pool.map          | no           yes            yes          yes
Pool.map_async    | no           yes            no           yes
Pool.apply        | yes          no             yes          no
Pool.apply_async  | yes          yes            no           no
Pool.starmap      | yes          yes            yes          yes
Pool.starmap_async| yes          yes            no           no

Uwagi:

  • Pool.imaporaz Pool.imap_async- leniwsza wersja mapy i map_async.

  • Pool.starmap metoda, bardzo podobna do metody mapy, poza tym akceptacja wielu argumentów.

  • Asyncmetody przesyłają wszystkie procesy naraz i pobierają wyniki po ich zakończeniu. Użyj metody get, aby uzyskać wyniki.

  • Pool.map(lub Pool.apply) metody są bardzo podobne do wbudowanej mapy Pythona (lub zastosowania). Blokują główny proces, dopóki wszystkie procesy się nie zakończą, i zwracają wynik.

Przykłady:

mapa

Jest wywoływany do listy zadań w jednym czasie

results = pool.map(func, [1, 2, 3])

zastosować

Można wezwać tylko do jednej pracy

for x, y in [[1, 1], [2, 2]]:
    results.append(pool.apply(func, (x, y)))

def collect_result(result):
    results.append(result)

map_async

Jest wywoływany do listy zadań w jednym czasie

pool.map_async(func, jobs, callback=collect_result)

Apply_async

Można wywołać tylko dla jednego zadania i równolegle wykonuje ono zadanie w tle

for x, y in [[1, 1], [2, 2]]:
    pool.apply_async(worker, (x, y), callback=collect_result)

starmap

Jest wariantem, pool.mapktóry obsługuje wiele argumentów

pool.starmap(func, [(1, 1), (2, 1), (3, 1)])

starmap_async

Kombinacja starmap () i map_async (), która iteruje po iterable iterable i wywołuje func z iterables po rozpakowaniu. Zwraca wynikowy obiekt.

pool.starmap_async(calculate_worker, [(1, 1), (2, 1), (3, 1)], callback=collect_result)

Odniesienie:

Znajdź pełną dokumentację tutaj: https://docs.python.org/3/library/multiprocessing.html

Rene B.
źródło
2
Pool.starmap () blokuje
Alan Evangelista