Хорошо, что вы указали в тегах Windows, это все объясняет. Под Windows нет простого способа "раздвоиться" процессу при вызове
multiprocessing.Process
, поэтому осуществляется очень сложная эмуляция этого поведения. При этом функция
target
выдирается из модуля, запускается в отдельном интерпретаторе, а все параметры сереализуются передаются и десереализуются перед вызовом
target
, при этом инициализация модуля в новом интерпретаторе выполняется частично (инициализируется только глобальный контекс). Подробнее об этом, например,
тут, есть еще одна очень хорошая статья где подробно рассмотрен этот механизм, но сейчас не найду ссылку.
Коротко о том, как готовить multiprocessing под Windows:
- Разделять процессы (вызов
multiprocessing.Process()
) как можно раньше в коде.
- По возможности избегать инициализации любых ресурсов и глобальных переменных до разделения. Учитывайте, что этот код выполняется во всех процессах независимо и может давать кучу сторонних эффектов.
- Не передавать через
args
никаких сложных объектов с "поведением" (кроме объектов из самого multiprocessing, он сам знает как их правильно передавать), только голые данные (примитивы или объекты состоящие только из примитивов), которые сериализуются без сторонних эффектов.
- Создавать дочерние процессы один раз, и на протяжении всего времени работать с ними посредством обмена сообщениями через Pipe/Queue. Не порождать новые процессы в цикле вычислений в момент "когда понадобятся".
- Queue при попытке записи/чтения может блокировать процесс, если при этом происходит запись/чтения в/из нее в другом процессе. (Думаю, именно это и происходит в коде в вопросе).
- Лучше использовать Pipe, который в худшем случае блокирует один процесс, а не все, как Queue.
- При создании процесса можно передавать ему два Pipe (input одного + output другого), в вызывающем процессе хранить соответствующие им коннекторы и только при помощи их общаться с дочерним процессом.
- Можно не делать
process.join()
, а просто читать результаты из output Pipe, они прочтутся только после того как попадут туда, что дальше будет происходить с процессом уже не важно (можно поставить return
после записи в Pipe в дочернем процессе).