Какие есть элементы конкурентности и параллельности и когда лучше использовать потоки, а когда процессы
Что проверяют
Понимаете ли вы разницу между конкурентностью и параллельностью и как на выбор в Python влияет GIL. Следом почти всегда спрашивают, где тут asyncio и что изменилось со сборкой без GIL.
Как отвечать
Конкурентность это ведение нескольких задач с переключением, пока одна ждёт. Параллельность это настоящее одновременное выполнение на разных ядрах. Первая возможна и на одном ядре, вторая нет.
Инструменты CPython:
asyncio: один поток, цикл событий и корутины. Переключение только наawait, поэтому тысячи соединений держатся дёшево. Нужны асинхронные библиотеки (httpx,asyncpg), одна блокирующая функция останавливает весь цикл;- потоки,
threadingиThreadPoolExecutor: общая память, переключает ОС. В обычной сборке GIL даёт исполнять байткод одному потоку, но отпускается на вводе-выводе и внутри многих C-расширений вроде NumPy. Хороши для I/O, но чистый Python не ускоряют; - процессы,
multiprocessingиProcessPoolExecutor: у каждого свой интерпретатор и свой GIL, путь для CPU-bound задач. Цена: запуск, сериализация аргументов черезpickle, нет общей памяти безshared_memory.
Правило: много ожидания сети и диска, берите asyncio или потоки; тяжёлые вычисления на чистом Python, процессы; вычисления в NumPy, может хватить и потоков.
Покажите, что следите за языком. В Python 3.13 появилась экспериментальная сборка без GIL (PEP 703), в 3.14 она получила официальную поддержку, но по умолчанию ставится сборка с GIL, и не все C-расширения с ней совместимы. В 3.14 же появились concurrent.interpreters и InterpreterPoolExecutor, а методом запуска процессов на Linux по умолчанию стал forkserver вместо fork.
Где спотыкаются
Говорят, что из-за GIL потоки в Python бесполезны. Для I/O-bound задач они работают отлично: на ожидании GIL отпускается.
Смешивают блокирующий код с asyncio, вызывают requests.get или time.sleep внутри корутины. Для таких вызовов есть asyncio.to_thread.
Передают в пул процессов лямбды и замыкания, которые не сериализуются через pickle, или гоняют через процессы мелкие задачи, где пересылка данных дороже самих вычислений.