concurrent.futures.ProcessPoolExecutor
использует модуль multiprocessing
для своей многопроцессорной обработки.
И, как объясняется в Руководства по программированию , это означает, что вы должны защититьлюбой код верхнего уровня, который вы не хотите запускать в каждом процессе в вашем __main__
блоке:
Убедитесь, что основной модуль может быть безопасно импортирован новым интерпретатором Python, не вызывая непреднамеренную сторонуэффекты (такие как запуск нового процесса).
... нужно защищать «точку входа» программы, используя if __name__ == '__main__':
…
Обратите внимание, что это тольконеобходимо, если используются spawn
или forkserver
методы запуска .Но если вы работаете в Windows, spawn
по умолчанию.И, во всяком случае, это никогда не делает больно делать это, и обычно делает код более понятным, так что в любом случае это стоит делать.
Вы, вероятно, не хотитечтобы защитить ваши import
с этим способом.В конце концов, стоимость вызова import pandas as pd
один раз для каждого ядра может показаться нетривиальной, но это происходит только при запуске, а стоимость запуска тяжелой функции, связанной с процессором, в миллионы раз полностью ее затопит.(Если нет, то вы, вероятно, не хотели использовать многопроцессорность в первую очередь…) И обычно то же самое относится к вашим операторам def
и class
(особенно, если они не захватывают какие-либо переменные замыкания или что-либо еще).Только код установки, который некорректно запускаться несколько раз (например, print('hello')
в вашем примере), должен быть защищен.
Примеры в concurrent.futures
документе (и в PEP3148 ) все справляются с этим с помощью идиомы "main function":
def main():
# all of your top-level code goes here
if __name__ == '__main__':
main()
Это дает дополнительное преимущество превращения ваших глобальных глобалов верхнего уровня в локальных, чтобы гарантировать, что вы случайно не поделитесьих (что может быть особенно проблематично с multiprocessing
, когда они на самом деле делятся с fork
, но копируются с spawn
, поэтому один и тот же код может работать при тестировании на одной платформе, но может не работать при развертывании на другой).
Если вы хотите узнать , почему это происходит:
С методом запуска fork
, multiprocessing
создает каждый новый дочерний процесс путем клонированияродительский интерпретатор Python, а затем просто запустить функцию обслуживания пула там, где вы (или concurrent.futures
) создали пул.Таким образом, код верхнего уровня не запускается повторно.
С помощью метода spawn
start multiprocessing
создает каждый новый дочерний процесс, запуская чистый новый интерпретатор Python, import
используя ваш коди затем запустите функцию обслуживания пула.Итак, код верхнего уровня перезапускается как часть import
.