Какие плюсы и минусы у многопоточности, многопроцессности и синхронности, и какие преимущества у асинхронного подхода над многопоточным
Что проверяют
Понимаете ли вы, что модель конкурентности выбирается по характеру нагрузки, а не по привычке. Вопрос почти всегда выводит на GIL, и следом идёт уточнение: сколько корутин вы поднимете против скольких потоков и что изменилось со свободнопоточными сборками.
Как отвечать
Разложите по типу нагрузки, а не по списку модулей:
- синхронный код: предсказуемый порядок и простая отладка. Плата в том, что на каждом сетевом вызове поток стоит и ждёт;
- потоки (
threading,ThreadPoolExecutor): хороши на I/O: GIL отпускается на время системного вызова. На счётной нагрузке выигрыша нет: интерпретатор переключает потоки примерно каждые 5 мс (sys.setswitchinterval), остаются накладные расходы плюс гонки и риск дедлока; - процессы (
multiprocessing,ProcessPoolExecutor): настоящий параллелизм по ядрам и изоляция падений. Цена: своя память под интерпретатор, старт процесса, передача данных черезpickle. В 3.14 стартовый метод на Linux по умолчаниюforkserver, а неfork: тот ломался в многопоточных программах; - async (
asyncio,async def/await,asyncio.TaskGroup): один поток, кооперативное переключение только наawait.
Преимущество async над потоками называйте свойствами: задача весит килобайты против мегабайтов на поток, поэтому десятки тысяч соединений держатся одним ядром, а переключение идёт в видимых в коде точках, и класс гонок исчезает без мьютексов. Минус тоже назовите: одна блокирующая строка (requests, time.sleep, тяжёлый расчёт) останавливает весь цикл, и выносить её нужно через asyncio.to_thread.
В 3.13 свободнопоточная сборка была экспериментом, в 3.14 она официально поддерживается, так что «потоки не параллелятся» уже требует оговорки.
Где спотыкаются
Говорят «из-за GIL потоки бесполезны». Бесполезны они на вычислениях, а на сетевой работе выигрывают, а большая часть веб-кода живёт именно там.
Кладут тяжёлый расчёт в корутину и получают таймауты сразу у всех клиентов: событийный цикл вытеснять некому, он ждёт возврата управления.
Обещают ускорение в число ядер от multiprocessing, забыв про сериализацию аргументов и результата: на мелких задачах пул процессов медленнее обычного цикла.