Короткий ответ
Threading выполняет потоки в одном процессе: они разделяют память и легко обмениваются данными, но для CPU-bound задач ограничены GIL. Multiprocessing запускает отдельные процессы со своей памятью и интерпретатором — они не ограничены GIL и действительно параллельно исполняются на разных ядрах.
Как это работает подробнее
Выбор между модулями — классический компромисс между лёгкостью и общим доступом к памяти (потоки) и изоляцией с настоящим параллелизмом (процессы).
- Потоки. Живут внутри одного процесса, создаются быстро, легко обмениваются данными через общую память, но требуют синхронизации блокировками и очередями.
- Процессы. Каждый имеет собственную память и свой интерпретатор, не ограничен GIL и может исполняться на отдельном ядре, однако создаётся тяжелее.
- Изоляция. Падение одного процесса не рушит остальные, но обмен данными между ними сложнее: очереди, pipe или общая память.
- Критерий выбора. Потоки предпочтительны для ввода-вывода, процессы — для вычислительных задач, которым нужны все ядра процессора.
Что использовать на практике
- I/O-bound. При ожидании сети, диска и API используй
threading. - CPU-bound. Для расчётов, которым нужны все ядра, используй
multiprocessing. - Общие данные. При работе потоков с общим состоянием применяй блокировки и очереди.
Подводные камни
- GIL-ограничение. Не жди ускорения вычислений от потоков: Python-байткод исполняет только один поток за раз.
- Стоимость процессов. Создание процесса дороже создания потока, поэтому на мелких задачах выигрыша не будет.
- Обмен данными. Между процессами нельзя просто разделить переменную — нужны очереди, pipe или разделяемая память.
Как отвечать на собеседовании
- Сравни потоки и процессы по осям: память, параллелизм и стоимость создания → привяжи выбор к типу задачи: I/O-bound — threading, CPU-bound — multiprocessing → упомяни GIL как ключевое ограничение потоков → добавь про синхронизацию и изоляцию.