сеньорчикОткрыть в Telegram
← все вопросывопросы для собеседований · Асинхронность в Python

Вопросы по асинхронности в Python на собеседовании

Асинхронность в Python спрашивают почти всегда, и почти всегда через GIL. Проверяют не определение, а понимание: почему async не ускоряет вычисления, что происходит при блокирующем вызове внутри корутины и когда всё-таки нужны процессы.

67 вопросов в банке·5 подтем·ниже разбор 9

Что спрашивают

Из чего состоит тема

Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.

Разборы подтем

Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.

Примеры вопросов с разбором

  1. #async_coordination1 / 9
    Зачем нужен asyncio.gather(*coros)?
    A)Выполнить корутины строго по очереди, по одной
    B)Ускорить CPU-счёт по разным ядрам
    C)Создать пул процессов и раскидать корутины по ядрам
    D)Запустить корутины конкурентно и собрать результаты
    показать ответ и разбор
    +D)Запустить корутины конкурентно и собрать результаты

    // разбор: gather планирует все переданные корутины/таски в цикле и ждёт их все, возвращая список результатов в порядке аргументов (не в порядке завершения). Это базовый способ сделать N I/O-операций одновременно. Ускорения CPU-счёта не даёт — всё в одном потоке под GIL.

  2. #async_event_loop2 / 9
    Что возвращает вызов async def-функции без await?
    A)Результат её выполнения
    B)Немедленно запускает тело функции в фоновом потоке
    C)None, пока не указан await
    D)Объект корутины, который ещё не запущен
    показать ответ и разбор
    +D)Объект корутины, который ещё не запущен

    // разбор: Вызов корутинной функции создаёт объект корутины и НЕ исполняет тело. Код внутри начнёт работать, только когда корутину отдадут циклу событий — через await, asyncio.run или create_task. Забытый await — типовой баг: корутина «висит», рантайм выдаёт «coroutine was never awaited».

  3. #async_pitfalls3 / 9
    Хендлер асинхронный, но под нагрузкой сервис встаёт колом:
    async def handler(u):
        r = requests.get(u)
        return r.json()
    Диагноз?
    A)Синхронный requests блокирует весь event loop
    B)requests не умеет .json()
    C)Не хватает await перед requests.get
    D).json() тоже надо await
    показать ответ и разбор
    +A)Синхронный `requests` блокирует весь event loop

    // разбор: requests.get — блокирующий вызов: он не отдаёт управление циклу, поэтому пока идёт запрос, ВСЕ корутины в этом потоке стоят. Один медленный бэкенд подвешивает весь сервис. Лечение: async-клиент (httpx.AsyncClient, aiohttp) с await либо вынос sync-вызова в run_in_executor.

  4. #gil4 / 9
    Что такое GIL в CPython?
    A)Жёсткий лимит на количество потоков в процессе
    B)Мьютекс: байткод исполняет один поток разом
    C)Сборщик мусора Python
    D)Флаг компиляции для параллелизма
    показать ответ и разбор
    +B)Мьютекс: байткод исполняет один поток разом

    // разбор: Global Interpreter Lock — глобальный мьютекс интерпретатора CPython: в любой момент байткод Python исполняет ровно один поток. Он упрощает управление памятью (refcount) ценой того, что потоки не считают Python-код параллельно на разных ядрах. На I/O и в C-расширениях GIL отпускается.

  5. #thread_vs_process5 / 9
    Чем multiprocessing обходит GIL?
    A)Каждый процесс — свой интерпретатор и свой GIL
    B)Переводит потоки в C
    C)Использует единый общий GIL, разделяемый всеми
    D)Отключает GIL в текущем процессе
    показать ответ и разбор
    +A)Каждый процесс — свой интерпретатор и свой GIL

    // разбор: multiprocessing поднимает отдельные процессы, у каждого свой интерпретатор Python и свой GIL — значит, они реально считают параллельно на разных ядрах. Плата: обмен данными идёт через пайпы и пиклинг (дороже общей памяти потоков), а старт процесса тяжелее старта потока.

  6. #async_coordination6 / 9
    await asyncio.gather(a(), b()), где a и b — по asyncio.sleep(1). Сколько ждём?
    A)Около 1 секунды
    B)Около 2 секунд
    C)Зависит от числа ядер
    D)Мгновенно, sleep не блокирует
    показать ответ и разбор
    +A)Около 1 секунды

    // разбор: Обе корутины спят на цикле одновременно: gather стартует их вместе, каждая на asyncio.sleep(1) отдаёт управление, цикл ждёт оба таймера параллельно — суммарно ~1 с, не 2. Заменить на синхронный time.sleep — цикл заблокируется, и выйдет ~2 с.

  7. #async_event_loop7 / 9
    Что делает await внутри корутины в точке ожидания?
    A)Запускает корутину в отдельном потоке
    B)Ускоряет исполнение, распределяя корутину по ядрам
    C)Отдаёт управление циклу, пока ждём результат
    D)Блокирует поток до готовности результата
    показать ответ и разбор
    +C)Отдаёт управление циклу, пока ждём результат

    // разбор: await в точке ожидания (I/O, таймер) отдаёт управление циклу событий: пока эта корутина ждёт, цикл крутит другие. Поток не блокируется — суть кооперативной многозадачности. Без await-точек корутина монополизирует цикл, и «асинхронность» пропадает.

  8. #async_pitfalls8 / 9
    Внутри корутины крутится тяжёлый Python-счёт на 500 мс. Что с сервисом?
    A)Цикл замирает на 500 мс — все запросы ждут
    B)Ничего, счёт идёт в фоне
    C)Другой поток подхватит счёт
    D)asyncio распараллелит его по ядрам
    показать ответ и разбор
    +A)Цикл замирает на 500 мс — все запросы ждут

    // разбор: Корутина без await-точек монополизирует единственный поток цикла: 500 мс счёта = 500 мс, когда цикл не обслуживает никого. Латентность всех запросов подскакивает. CPU-счёт в async-сервисе выносят в пул процессов через loop.run_in_executor(ProcessPoolExecutor).

  9. #gil9 / 9
    Ускорят ли потоки чисто вычислительную (CPU-bound) задачу в CPython?
    A)Только вместе с asyncio
    B)Нет: GIL сериализует исполнение байткода
    C)Да, но только для float
    D)Да, ускоряют почти линейно по числу ядер
    показать ответ и разбор
    +B)Нет: GIL сериализует исполнение байткода

    // разбор: Для CPU-bound Python-кода потоки не ускоряют: GIL пускает исполнять байткод по одному потоку, остальные ждут — на 4 ядрах всё равно ~1 ядро работы плюс накладные на переключение. Ускорение дают процессы (multiprocessing) или C-расширения, отпускающие GIL (numpy).

это 9 из 67

Ещё 58 вопросов по теме — в тренажёре, с движком повторения

Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.

Частые вопросы