Использование многопроцессорного пула python в терминале и в модулях кода для Django или Flask
при использовании многопроцессорной обработки.Пул в python со следующим кодом, есть некоторое странное поведение.
from multiprocessing import Pool
p = Pool(3)
def f(x): return x
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
try: print(t.get(timeout=1))
except Exception: pass
Я получаю следующую ошибку три раза (по одному для каждого потока в пуле), и он печатает "3" через "19":
AttributeError: 'module' object has no attribute 'f'
первые три вызова apply_async никогда не возвращаются.
между тем, если я попробую:
from multiprocessing import Pool
p = Pool(3)
def f(x): print(x)
p.map(f, range(20))
Я получаю AttributeError 3 раза, оболочка печатает "6" через "19" , а затем зависает и не может быть убита [Ctrl] + [C]
многопроцессорные документы имеют следующее, чтобы сказать:
функциональность в этом пакете требует, чтобы main модуль и дети тоже.
что это значит?
чтобы уточнить, я запускаю код в терминале для тестирования функциональности, но в конечном итоге я хочу иметь возможность поместить это в модули веб-сервера. Как правильно использовать многопроцессорность.Пул в терминале python а в кодовых модулях?
3 ответов
это означает, что пулы должны быть инициализированы после определения функций, которые будут выполняться на них. Использование пулов в if __name__ == "__main__":
блоки работают, если вы пишете автономный скрипт, но это невозможно ни в больших базах кода, ни в коде сервера (например, в проекте Django или Flask). Итак, если вы пытаетесь использовать пулы в одном из них, обязательно следуйте этим рекомендациям, которые объясняются в разделах ниже:
- инициализировать пулы в нижней части модули или внутренние функции.
- не вызывайте методы пула в глобальной области модуля.
кроме того, если вам нужен только лучший параллелизм при вводе/выводе (например, доступ к базе данных или сетевые вызовы), вы можете сэкономить всю эту головную боль и использовать пулы потоков вместо пулов процессов. Это включает в себя полностью недокументированные:
from multiprocessing.pool import ThreadPool
это интерфейс точно такой же, как у пула, но поскольку он использует потоки, а не процессы, он поставляется без каких-либо предостережений, что использование пулов процессов, с единственным недостатком, вы не получаете истинный параллелизм выполнения кода, просто параллелизм в блокировке ввода-вывода
пулы должны быть инициализированы после определения функций, которые будут выполняться на них
непостижимый текст из документов python означает, что во время определения пула окружающий модуль импортируется потоками в пуле. В случае терминала python, это означает, что все и только код, который вы запустили до сих пор.
и любые функции, которые вы хотите использовать в пуле, должны быть определены до инициализации пула. Это верно как для кода в модуле, так и для кода в терминале. Следующие изменения кода в вопросе будут работать нормально:
from multiprocessing import Pool
def f(x): return x # FIRST
p = Pool(3) # SECOND
threads = [p.apply_async(f, [i]) for i in range(20)]
for t in threads:
try: print(t.get(timeout=1))
except Exception: pass
или
from multiprocessing import Pool
def f(x): print(x) # FIRST
p = Pool(3) # SECOND
p.map(f, range(20))
под штрафом я имею в виду штраф в Unix. У Windows есть свои проблемы, в которые я не собираюсь здесь.
предостережения к использованию пулов в модулях
но подождите, есть еще (для использования пулов в модулях, которые вы хотите импортировать в другом месте)!
если вы определяете пул внутри функции, у вас нет проблем. но если вы используете объект пула в качестве глобальной переменной в модуле, он должен быть определен в дно страницы, а не сверху. Хотя это противоречит самому хорошему стилю кода, это необходимо для функциональности. Способ использования пула, объявленного в верхней части страницы, - использовать его только с функциями, импортированными из других модулей, например:
from multiprocessing import Pool
from other_module import f
p = Pool(3)
p.map(f, range(20))
импорт предварительно настроенного пула из другого модуля довольно ужасен, так как импорт должен происходить после того, что вы хотите запустить на нем, например:
### module.py ###
from multiprocessing import Pool
POOL = Pool(5)
### module2.py ###
def f(x):
# Some function
from module import POOL
POOL.map(f, range(10))
и во-вторых, если вы запускаете что-либо в пуле в глобальной области импортируемого модуля, система зависает. то есть это не работа:
### module.py ###
from multiprocessing import Pool
def f(x): return x
p = Pool(1)
print(p.map(f, range(5)))
### module2.py ###
import module
это, однако, тут работа, пока ничего не импортирует module2:
### module.py ###
from multiprocessing import Pool
def f(x): return x
p = Pool(1)
def run_pool(): print(p.map(f, range(5)))
### module2.py ###
import module
module.run_pool()
теперь причины этого только более причудливы и, вероятно, связаны с тем, что код в вопросе только выплевывает ошибку атрибута один раз и после этого, кажется, правильно выполняет код. Похоже, что потоки пула (по крайней мере, с некоторой надежностью) перезагружают код в модуле после выполнения. Феноменальный.
функция, которую вы хотите выполнить в пуле потоков, должна быть уже определена при создании пула.
это должно работать:
from multiprocessing import Pool
def f(x): print(x)
if __name__ == '__main__':
p = Pool(3)
p.map(f, range(20))
причина в том, что (по крайней мере, на системах с fork
) при создании пула рабочие создаются путем разветвления текущего процесса. Поэтому, если целевая функция еще не определена в этот момент, работник не сможет ее вызвать.
в windows это немного отличается, так как windows не имеет fork
. Здесь запускаются новые рабочие процессы и импортируется основной модуль. Вот почему в windows важно защитить исполняемый код с помощью if __name__ == '__main__'
. В противном случае каждый новый работник будет пересматривать код и, следовательно, порождать новые процессы бесконечно, сбой программы (или системы).
есть еще один возможный источник этой ошибки. Я получил эту ошибку при запуске примера кода.
источником было то, что, несмотря на правильную установку многопроцессорной обработки, компилятор C++ не был установлен в моей системе, что-то pip сообщил мне при попытке обновить многопроцессорную обработку. Поэтому, возможно, стоит проверить, установлен ли компилятор.