Я бы не назвал concurrent.futures
более «продвинутым» - это более простой интерфейс, который работает практически одинаково, независимо от того, используете ли вы несколько потоков или несколько процессов в качестве основного трюка распараллеливания.
Таким образом, как и практически во всех случаях «более простого интерфейса», в нем присутствуют практически одни и те же компромиссы: он имеет более мелкую кривую обучения, в значительной степени потому, что гораздо меньше доступно для изучения ; но, поскольку он предлагает меньше вариантов, он может в конечном итоге разочаровать вас так, как этого не сделает более богатый интерфейс.
Что касается задач, связанных с ЦП, то это слишком недооценено, чтобы говорить о значении. Для задач с привязкой к процессору в CPython вам нужно несколько процессов, а не несколько потоков, чтобы иметь какой-либо шанс получить ускорение. Но степень (если таковая имеется) ускорения зависит от деталей вашего оборудования, вашей ОС и особенно от того, сколько межпроцессного взаимодействия требуют ваши конкретные задачи. Под прикрытием все уловки межпроцессного распараллеливания основаны на одних и тех же примитивах ОС - высокоуровневый API, который вы используете для достижения этих целей, не является основным фактором, влияющим на конечную скорость.
Изменить: пример
Вот окончательный код, показанный в статье, на которую вы ссылались, но я добавляю оператор импорта, необходимый для его работы:
from concurrent.futures import ProcessPoolExecutor
def pool_factorizer_map(nums, nprocs):
# Let the executor divide the work among processes by using 'map'.
with ProcessPoolExecutor(max_workers=nprocs) as executor:
return {num:factors for num, factors in
zip(nums,
executor.map(factorize_naive, nums))}
Вот точно так же, используя multiprocessing
вместо этого:
import multiprocessing as mp
def mp_factorizer_map(nums, nprocs):
with mp.Pool(nprocs) as pool:
return {num:factors for num, factors in
zip(nums,
pool.map(factorize_naive, nums))}
Обратите внимание, что возможность использовать multiprocessing.Pool
объекты в качестве контекстных менеджеров была добавлена в Python 3.3.
С кем легче работать? LOL ;-) Они практически идентичны.
Отличие заключается в том, что Pool
поддерживает так много различных способов сделать вещи , которые вы не можете понять , как легко может быть до тех пор , пока вы поднимались довольно далеко вверх по кривой обучения.
Опять же, все эти разные способы являются одновременно и силой, и слабостью. Они сильны, потому что в некоторых ситуациях может потребоваться гибкость. Они слабость из-за «желательно только одного очевидного способа сделать это». Проект, который будет придерживаться исключительно (если возможно) concurrent.futures
, вероятно, будет легче поддерживать в долгосрочной перспективе, из-за отсутствия беспричинной новизны в том, как можно использовать его минимальный API.
ProcessPoolExecutor
самом деле имеет больше параметров, чемPool
потому, чтоProcessPoolExecutor.submit
возвращаетFuture
экземпляры, которые позволяют cancellation (cancel
), проверять, какое исключение было вызвано (exception
), и динамически добавлять обратный вызов, который будет вызван по завершении (add_done_callback
). Ни одна из этих функций не доступна сAsyncResult
экземплярами, возвращеннымиPool.apply_async
. В других отношенияхPool
имеет больше возможностей из - заinitializer
/initargs
,maxtasksperchild
иcontext
вPool.__init__
, и больше способов , предоставляемые ,Pool
например.Pool
, а о модулях.Pool
это небольшая часть того, что находится внутриmultiprocessing
, и настолько далеко в документах, что людям нужно время, чтобы осознать, что оно вообще существуетmultiprocessing
. Этот конкретный ответ сфокусирован наPool
том, что это вся статья, с которой связан ОП, и с которойcf
«намного проще работать» просто не соответствует тому, что обсуждалась в статье. Кроме того,cf
этоas_completed()
также может быть очень удобно.