multiprocessing.Pool: Когда использовать apply, apply_async или map?

Ответы:

426

В старые времена 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, результаты возвращаются в порядке, соответствующем порядку аргументов.

unutbu
источник
11
Должно ли быть if __name__=="__main__"раньше apply_async_with_callback()на Windows?
JFS
3
Большое спасибо. как насчет map_async?
Phyo Arkar Lwin
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

Относительно applyпротив map:

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

pool.map(f, iterable)Этот метод разделяет итерируемое на несколько частей, которые он отправляет в пул процессов как отдельные задачи. Таким образом, вы используете все процессы в пуле.

kakhkAtion
источник
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)

Starmap

Есть вариант, pool.mapкоторый поддерживает несколько аргументов

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

starmap_async

Комбинация starmap () и map_async (), которая перебирает итерируемые итерируемые элементы и вызывает func с неупакованными итераблами. Возвращает объект результата.

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 () блокируется
Алан Евангелиста