multiprocessing.Pool: Коли користуватися застосувати, apply_async чи карту?


Відповіді:


424

Ще в старі часи Python, для виклику функції з довільними аргументами, ви використовуєте apply:

apply(f,args,kwargs)

applyвсе ще існує в Python2.7, хоча не є в Python3, і, як правило, більше не використовується. У наш час

f(*args,**kwargs)

є кращим. У multiprocessing.Poolмодулях намагаються забезпечити подібний інтерфейс.

Pool.applyє як Python apply, за винятком того, що виклик функції виконується в окремому процесі. Pool.applyблокує, поки функція не буде виконана.

Pool.apply_asyncтакож як вбудований Python apply, за винятком того, що дзвінок повертається негайно, замість того, щоб чекати результату. AsyncResultОб'єкт повертається. Ви викликаєте його get()метод для отримання результату виклику функції. У get()методі блокується , поки ця функція буде завершено. Таким чином, pool.apply(func, args, kwargs)еквівалентно pool.apply_async(func, args, kwargs).get().

На відміну Pool.applyвід Pool.apply_asyncметоду також є зворотний виклик, який, якщо він надається, викликається, коли функція завершена. Це можна використовувати замість дзвінка get().

Наприклад:

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()

може дати такий результат, як

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

Зауважте, на відміну від цього pool.map, порядок результатів може не відповідати порядку, в якому здійснювались pool.apply_asyncдзвінки.


Отже, якщо вам потрібно запустити функцію в окремому процесі, але хочете, щоб поточний процес блокувався, поки ця функція не повернеться, використовуйте Pool.apply. Мовляв Pool.apply, Pool.mapблокує, поки не повернеться повний результат.

Якщо ви хочете, щоб пул робочих процесів виконував безліч викликів функцій асинхронно, використовуйте Pool.apply_async. Порядок результатів не гарантується бути такою ж , як і порядок дзвінків в Pool.apply_async.

Зауважте також, що ви можете зателефонувати на декілька різних функцій Pool.apply_async(не всі дзвінки повинні використовувати одну і ту ж функцію).

На відміну від цього, Pool.mapзастосовується та ж функція до багатьох аргументів. Однак, на відміну від Pool.apply_async, результати повертаються у порядку, що відповідає порядку аргументів.


11
Чи повинні бути if __name__=="__main__"раніше apply_async_with_callback()в Windows?
jfs

3
Дуже дякую. як щодо map_async?
Пхіо Аркар Лвін

38
Погляньте всередині multiprocessing / pool.py, і ви побачите, що Pool.map(func,iterable)еквівалентно Pool.map_async(func,iterable).get(). Таким чином, зв'язок між Pool.mapі Pool.map_asyncє подібним до Pool.applyта Pool.apply_async. Ці asyncкоманди повертають негайно, в той час як не- asyncкоманди блокувати. Ці asyncкоманди також мають функцію зворотного виклику.
unutbu

7
Вибір між використанням Pool.mapта Pool.applyподібним до вирішення, коли використовувати mapабо applyв Python. Ви просто використовуєте інструмент, який відповідає роботі. Вибір між використанням asyncта не- asyncверсією залежить від того, чи хочете виклик заблокувати поточний процес та / або ви хочете використовувати зворотний дзвінок.
unutbu

6
@falsePockets: Так. Кожен дзвінок apply_asyncповертає ApplyResultоб'єкт. Виклик , який ApplyResult«S getметод поверне значення, що повертається асоційованої функції (або рейз , mp.TimeoutErrorякщо час-виклик з.) Так що, якщо ви поклали ApplyResultз в упорядкованому списку, то виклик їх getметоди будуть повертати результати в тому ж порядку. pool.mapОднак ви просто можете використовувати в цій ситуації.
unutbu

75

Щодо applyvs map:

pool.apply(f, args): fвиконується лише в ОДИН із працівників пулу. Отже ОДИН із процесів у пулі буде запускатися f(args).

pool.map(f, iterable): Цей метод розбиває ітерабельний ряд на кілька фрагментів, які він подає до пулу процесів як окремі завдання. Таким чином, ви скористаєтеся всіма процесами в пулі.


4
що робити, якщо ітерабельний генератор
RustyShackleford

Хм ... Добре запитання. Якщо чесно , я ніколи не використовував басейни з генераторами, але цей потік може бути корисно: stackoverflow.com/questions/5318936 / ...
kakhkAtion

@kakhkAtion Що стосується застосування, якщо лише один із працівників виконує функцію, що робити з іншими працівниками? Чи потрібно мені зателефонувати подати заявку кілька разів, щоб решта працівників виконали завдання?
Мондра

3
Правда. Також погляньте на pool.apply_async, якщо ви хочете обідати працівників асинхронно. "pool_apply блокує, поки результат не буде готовий, тому apply_async () краще підходить для виконання робіт паралельно"
kakhkAtion

1
Що відбувається, коли у мене є 4 процеси, але я викликав apply_async()8 разів? Чи автоматично оброблятиме його з чергою?
Сараванабалагі Рамачандран

31

Ось короткий огляд у вигляді таблиці для того , щоб показати різницю між Pool.apply, Pool.apply_async, Pool.mapі Pool.map_async. Вибираючи один з них, ви повинні враховувати багатоагрегати, паралельність, блокування та замовлення:

                  | 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

Примітки:

  • Pool.imapі Pool.imap_async- лазерна версія карти та map_async.

  • Pool.starmap метод, дуже схожий на метод map, крім того, що він приймає кілька аргументів.

  • Asyncметоди подають всі процеси одразу та отримують результати, як тільки вони закінчуються. Для отримання результатів використовуйте метод get.

  • Pool.map(або Pool.apply) методи дуже схожі на вбудовану карту Python (або застосувати). Вони блокують основний процес, поки всі процеси не завершаться і повернуть результат.

Приклади:

карта

Викликається за перелік робочих місць за один раз

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

застосовувати

Можна закликати лише до однієї роботи

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

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

map_async

Викликається за перелік робочих місць за один раз

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

apply_async

Можна викликати лише одну роботу та паралельно виконувати завдання у фоновому режимі

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

зірка

Це варіант, pool.mapякий підтримує кілька аргументів

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

starmap_async

Поєднання starmap () та map_async (), яке перетворює ітерабельні ітерабелі та викликає функцію з розпакованим ітерабелем. Повертає об'єкт результату.

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

Довідка:

Повну документацію можна знайти тут: https://docs.python.org/3/library/multiprocessing.html


2
Pool.starmap () блокує
Алан Евангеліста
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.