Использование многопроцессорного пула 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). Итак, если вы пытаетесь использовать пулы в одном из них, обязательно следуйте этим рекомендациям, которые объясняются в разделах ниже:

  1. инициализировать пулы в нижней части модули или внутренние функции.
  2. не вызывайте методы пула в глобальной области модуля.

кроме того, если вам нужен только лучший параллелизм при вводе/выводе (например, доступ к базе данных или сетевые вызовы), вы можете сэкономить всю эту головную боль и использовать пулы потоков вместо пулов процессов. Это включает в себя полностью недокументированные:

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 сообщил мне при попытке обновить многопроцессорную обработку. Поэтому, возможно, стоит проверить, установлен ли компилятор.