Какие ограничения есть у multiprocessing и чем fork отличается от spawn?
- Почему ProcessPoolExecutor не принимает лямбду?
- Как процессы в Python обмениваются данными?
Процессы не делят память: аргументы и результаты сериализуются pickle и копируются. Лямбды, локальные функции, открытые файлы и соединения не сериализуются. fork копирует процесс родителя (быстро, но опасно с потоками), spawn запускает чистый интерпретатор и импортирует модуль заново (нужна защита if __name__ == "__main__").
- По умолчанию: Linux — fork (с 3.14 — forkserver), macOS и Windows — spawn
- Общие данные — Queue, Pipe, Manager, shared_memory
- Большие данные гонять через pickle дорого — передавать пути или использовать shared_memory
А ещё fork после запуска потоков может унаследовать захваченную блокировку и зависнуть
Подробно
Почему pickle. Процесс — отдельное адресное пространство. Чтобы передать задачу, пул сериализует функцию (по имени модуля и функции) и аргументы, отправляет по каналу, а результат — обратно. Поэтому:
- функция должна импортироваться по имени — не лямбда и не вложенная;
- аргументы — сериализуемые (не сокеты, не соединения с БД, не генераторы);
- каждое копирование стоит времени: мелкие задачи с большими данными выполняются медленнее, чем последовательно.
Методы запуска:
- fork — копия родителя с его памятью (copy-on-write): быстрый старт, глобальные данные доступны. Опасен, если в родителе уже были потоки: в ребёнке остаётся только один поток, а блокировки, которые держали другие, — захваченными навсегда.
- spawn — новый интерпретатор, модуль импортируется заново. Медленнее, зато предсказуемо. Код запуска — под
if __name__ == "__main__":, иначе дети рекурсивно запускают пул. - forkserver — компромисс: форк от чистого серверного процесса.
Обмен данными: multiprocessing.Queue, Pipe, Manager (прокси к объектам в процессе-сервере, медленно), shared_memory (Python 3.8) — общий буфер без копирования, удобно с NumPy.
Что хотят услышать
- Отсутствие общей памяти и роль pickle.
- Ограничения на функции и аргументы.
- fork, spawn и их риски.
Частые ошибки
- Запуск пула без if __name__ == "__main__".
- Передача огромных объектов в каждую задачу.
Проверьте себя
Несколько верных вариантов. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓Аргументы и результаты сериализуются pickle и копируются
- ✗Процессы делят общую память, поэтому аргументы не копируются и pickle не нужен
- ✓Лямбды и локальные функции в процесс не передать
- ✓При spawn нужна защита if __name__ == "__main__"
Разбор темы
- Потоки, процессы и GIL
Что такое GIL и что он не защищает, когда выбирать потоки, процессы или asyncio, concurrent.futures, ограничения multiprocessing, сборка без GIL.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.