Короткий ответ ThreadPoolExecutor использует потоки в одном процессе и подходит для I/O-bound задач; ProcessPoolExecutor использует отдельные процессы с собственным GIL каждый, подходит для CPU-bound задач, но добавляет накладные расходы на межпроцессную передачу данных. Как это работает подробнее Оба предоставляют одинаковый интерфейс (submit, map, Future), но ThreadPoolExecutor создаёт threading.Thread внутри процесса, а ProcessPoolExecutor — отдельные процессы, между которыми аргументы и результаты сериализуются через IPC. Пример кода Что использовать на практике Практика. Для большого числа одновременных сетевых запросов — ThreadPoolExecutor (или asyncio, если библиотеки поддерживают async); для тяжёлых вычислений на чистом Python без C-расширений, освобождающих GIL — ProcessPoolExecutor, если прирост от параллелизма превышает издержки передачи данных. Подводные камни Риски. Используют ProcessPoolExecutor для задач с большим объёмом данных на вход/выход каждой задачи — издержки сериализации съедают выигры