См. Пример и результат выполнения ниже:
#!/usr/bin/env python3.4
from multiprocessing import Pool
import time
import os
def initializer():
print("In initializer pid is {} ppid is {}".format(os.getpid(),os.getppid()))
def f(x):
print("In f pid is {} ppid is {}".format(os.getpid(),os.getppid()))
return x*x
if __name__ == '__main__':
print("In main pid is {} ppid is {}".format(os.getpid(), os.getppid()))
with Pool(processes=4, initializer=initializer) as pool: # start 4 worker processes
result = pool.apply(f, (10,)) # evaluate "f(10)" in a single process
print(result)
#result = pool.apply_async(f, (10,)) # evaluate "f(10)" in a single process
#print(result.get())
Дает:
$ ./pooleg.py
In main pid is 22783 ppid is 19542
In initializer pid is 22784 ppid is 22783
In initializer pid is 22785 ppid is 22783
In initializer pid is 22787 ppid is 22783
In f pid is 22784 ppid is 22783
In initializer pid is 22786 ppid is 22783
100
Как видно из вывода: было создано 4 процесса, но только один из них действительно работал (названный f).
Вопрос: Зачем мне создавать пул из> 1 воркера и вызывать apply(), если работа f выполняется только одним процессом? То же самое и с apply_async(), потому что и в этом случае работа выполняется только одним работником.
Я не понимаю, в каких случаях могут быть полезны эти функции.
@salparadise Тот же результат. инициализатор не имеет значения.
это делает для меня. Чтобы быть ясным, Pool(processes=4) as pool - это то, как я позвонил.
@salparadise После удаления инициализатора f все еще вызывает только один процесс.






Эта строка в вашем коде:
Pool(processes=4, initializer=initializer) as pool: # start 4 worker processes
не Начните 4 рабочих процесса. Он просто создает пул из них, который может поддерживать одновременную работу такого большого количества из них. Такие методы, как apply(), фактически запускают отдельные процессы.
Разница в том, что apply() и apply_async() в том, что первый блокируется до тех пор, пока результат не будет готов, а второй сразу же возвращает объект «результат». Это не имеет большого значения, если вы не хотите отправлять более одной задачи на Pool за раз (что, конечно же, составляет весь смысл использования модуля multiprocessing).
Вот некоторые модификации вашего кода, показывающие, как на самом деле выполнять некоторую параллельную обработку с Pool:
from multiprocessing import Pool
import time
import os
def initializer():
print("In initializer pid is {} ppid is {}".format(os.getpid(),os.getppid()))
def f(x):
print("In f pid is {} ppid is {}".format(os.getpid(),os.getppid()))
return x*x
if __name__ == '__main__':
print("In main pid is {} ppid is {}".format(os.getpid(), os.getppid()))
with Pool(processes=4, initializer=initializer) as pool: # Create 4 worker Pool.
# result = pool.apply(f, (10,)) # evaluate "f(10)" in a single process
# print(result)
# Start multiple tasks.
tasks = [pool.apply_async(f, (val,)) for val in range(10, 20)]
pool.close() # No more tasks.
pool.join() # Wait for all tasks to finish.
results = [result.get() for result in tasks] # Get the result of each.
print(results)
map_sync() лучше подходит для обработки чего-то вроде этого (последовательности значений), поскольку он автоматически обрабатывает некоторые детали, показанные в приведенном выше коде.
Во-первых, оба предназначены для работы с кортежами аргументов (одиночные вызовы функций), в отличие от вариантов Pool.map, которые работают с итерациями. Так что это не ошибка, когда вы наблюдаете только один процесс, используемый при однократном вызове этих функций.
Вы бы использовали Pool.apply_async вместо одной из версий Pool.map, где вам нужен более точный контроль над отдельными задачами, которые вы хотите распределить.
Версии Pool.map берут итерацию и разбивают их на задачи, где каждая задача имеет целевую функцию тем же (сопоставленную).
Pool.apply_async обычно не вызывается только один раз с пулом из> 1 воркера. Поскольку это асинхронно, вы можете перебирать вручную предварительно собранные задачи и отправлять их в несколько
рабочие процессы до того, как любой из них будет завершен. Ваш список задач здесь может состоять из различных целевых функций, как вы можете видеть в этом ответе здесь. Он также позволяет регистрировать обратные вызовы для результатов и ошибок, как в примере это.
Эти свойства делают Pool.apply_async довольно универсальным и лучшим инструментом для сценариев проблем необычный, которые невозможно решить с одной из версий Pool.map.
Pool.apply действительно не очень полезен на первый взгляд (и второй). Вы можете использовать его для синхронизации потока управления в сценарии, когда вы сначала запускаете несколько задач с apply_async, а затем имеете задачу, которая должна быть завершена, прежде чем вы запускаете еще один раунд задач с apply_async.
Использование Pool.apply также может означать избавление вас от необходимости создания одного дополнительного процесса для промежуточной задачи, когда у вас уже есть пул, который в настоящее время простаивает.
Я думаю, вы правы в том, что единственное использование Pool.apply или Pool.apply_async - это когда у вас есть пул и вы хотите запустить в нем один процесс, не перегружая всю доступность, тогда как Pool.map и варианты по умолчанию будут использовать как можно больше процессов. Об этом следует более четко упомянуть в основной документации.
@jamadagni Pool.apply_async() используется не только для одиночных заданий, он часто используется, например, заполнение списка "фьючерсами", которые вы получите от звонка. Смысл продажи - большая гибкость и отсутствие блокировки основного потока в родительском. Это более гибкий подход, потому что вы можете определить отдельную функцию для каждого отдельного элемента, который будет обрабатываться (пример), и у вас также есть параметр callback для действия JIT над результатом.
пробовать без
initializer?