#python #parallel-processing #apply #python-multiprocessing
#python #параллельная обработка #применить #python-многопроцессорность
Вопрос:
Последние несколько дней я пытался понять многопроцессорность (не многопоточность) в Python и не понимаю цели multiprocessing.pool.Pool.apply().
Поскольку это первый метод Pool
класса, я бы предположил, что он используется по умолчанию, но он кажется бесполезным в контексте многопроцессорной обработки. Документация запутанная и очень краткая и гласит:
Он блокируется до тех пор, пока результат не будет готов. Учитывая эти блоки, apply_async() лучше подходит для параллельного выполнения работы.
- Блокирует ли он только процесс, который был создан для него? Если да, то это не будет проблемой для процессов, в основном связанных с процессором.
Кроме того, функция выполняется только в одном из рабочих элементов пула.
- Почему что-то подобное должно быть даже в
Pool
классе? Когда я впервые прочитал это, я подумал, что неправильно понял, но на самом деле кажется, что запущен только один процесс. Зачем кому-то такое поведение, если он использует класс специально для многопроцессорной обработки?
Я знаю, что есть apply_async
и, похоже, это работает, но у меня все еще остается ощущение, что я, должно быть, что-то упускаю apply
, и я хочу это прояснить.
Я использую код, подобный этому:
with Pool(16) as pool:
pool_results = []
for city in cities:
for street in streets:
for house in houses:
for room in rooms:
pool_results.append(pool.apply_async(func=simulate, args=(city, street, house, room)))
[result.wait() for result in pool_results]
Комментарии:
1. Вы неправильно поняли некоторые вещи (например, когда запускаются процессы или сколько их запущено), но
Pool.apply
это действительно довольно бесполезно.2. Подумайте о
Pool.apply
том же, о чем вы думаетеThread.run
(когда не переопределяете его) — как о строительном блоке, который используется под капотом для составления поведения, которое вы в конечном итоге хотите, в отличие от чего-то, что фактически обеспечивает само это поведение. Одна из вещей, которая выпадает из Python, не имеющего частных методов, заключается в том, что вы можете видеть все на поверхности — просто потому, что метод существует, не означает, что он существует с учетом прямого вызова конечного пользователя.3. @CharlesDuffy: Я не знаком со спецификой
Thread.run
, но если это действительно строительный блок, то почему он должен быть частью API «высокого уровня»? Кажется плохим и запутанным выбором дизайна.4. Если вы создаете высокоуровневый API, обычно создаются вспомогательные функции для его поддержки. Они попадают туда как часть структуры поддержки для вызовов высокого уровня; но сами по себе не являются вызовами высокого уровня. Я хочу сказать, что на самом деле здесь нет проблемы. Ничто не обязывает вас использовать вызовы, о которых вы не заботитесь, и если они оказались полезными для человека, который создавал вызовы, которые вас волнуют , зачем утверждать, что этот человек был небрежен?
5. …Я также отмечу, что синхронный
apply
дает возможность иметь поток внутри одного блока процесса, когда что-то происходит в члене пула в другом процессе. Это может быть удобно, если вы хотите создать свой фреймворк пользовательского интерфейса в потоковой модели с общей памятью, в то время как ваша фактическая работа выполняется в рамках многопроцессорной модели. Нередко люди предпочитают многопоточность, когда это может сойти им с рук; использование многопроцессорной обработки для переноса работы, которая удерживала бы GIL вне процесса, позволяет избежать написания традиционного кода на основе потоков, когда иначе это невозможно.