Я пытался распараллелить некоторый код, используя concurrent.futures.ProcessPoolExecutor
, но у меня продолжались странные взаимоблокировки, которых не было с ThreadPoolExecutor
. Минимальный пример:
from concurrent import futures
def test():
pass
with futures.ProcessPoolExecutor(4) as executor:
for i in range(100):
print('submitting {}'.format(i))
executor.submit(test)
В python 3.2.2 (на 64-битной Ubuntu) это, кажется, постоянно зависает после отправки всех заданий - и это, кажется, происходит всякий раз, когда количество отправленных заданий превышает количество рабочих. Если я заменю ProcessPoolExecutor
на ThreadPoolExecutor
, он будет работать безупречно.
В качестве попытки разобраться, я дал каждому future-объекту обратный вызов, чтобы вывести значение i
:
from concurrent import futures
def test():
pass
with futures.ProcessPoolExecutor(4) as executor:
for i in range(100):
print('submitting {}'.format(i))
future = executor.submit(test)
def callback(f):
print('callback {}'.format(i))
future.add_done_callback(callback)
Это еще больше сбило меня с толку - напечатанное значение i
by callback
- это значение во время его вызова, а не во время его определения (поэтому я никогда не вижу callback 0
, но я получаю много callback 99
с). Опять же, ThreadPoolExecutor
выводит ожидаемое значение.
Интересно, может ли это быть ошибкой, я попробовал последнюю версию python для разработки. Теперь код, по крайней мере, кажется завершенным, но я все равно получаю неверное значение i
.
Кто-нибудь может объяснить:
что случилось с ProcessPoolExecutor
между python 3.2 и текущей версией разработчика, которая, по-видимому, устранила этот тупик
, почему «неправильное» значение i
печатается
РЕДАКТИРОВАТЬ: как указал ниже Юкевич, конечно, печать i
напечатает значение во время вызова обратного вызова, я не знаю, о чем я думал ... если я передам вызываемый объект со значением i
в качестве одного из его атрибутов, это будет работать должным образом.
РЕДАКТИРОВАТЬ: немного больше информации: выполняются все обратные вызовы, поэтому похоже, что это executor.shutdown
(вызывается исполнителем .__ exit __
), который не может сказать, что процессы завершены. Кажется, что это полностью исправлено в текущем Python 3.3, но, похоже, было много изменений в multiprocessing
и concurrent.Futures
, поэтому я не знаю, что это исправило. Поскольку я не могу использовать 3.3 (похоже, он несовместим ни с выпуском, ни с версией numpy для разработчиков), я попытался просто скопировать его многопроцессорные и параллельные пакеты в свою установку 3.2, которая, похоже, работает нормально. Тем не менее, кажется немного странным, что - насколько я могу судить - ProcessPoolExecutor
полностью не работает в последней версии выпуска, но это не касается других.