В каких случаях асинхронность не подходит из-за конкуренции за ресурсы
Что проверяют
Понимаете ли вы, что asyncio ускоряет ожидание, а не работу, и что упор в общий ограниченный ресурс он не снимает. Следом обычно спрашивают, как ограничить параллелизм и что делать с CPU-задачей внутри асинхронного сервиса.
Как отвечать
asyncio это один поток и цикл событий, задачи переключаются только на await. Выигрыш есть, когда задачи в основном ждут сеть. Плохо он работает в таких случаях:
- узкий ресурс с фиксированной ёмкостью: пул соединений к базе на 20 штук, внешний API с лимитом запросов, диск. Запуск 10 000 корутин через
asyncio.gatherне сделает ресурс шире: задачи встанут в очередь на пул, начнутся тайм-ауты,too many connectionsили ответы 429; - конкуренция за одни данные: если тысячи задач обновляют одну строку, упор идёт в блокировки строк в базе, и асинхронный клиент тут ничем не поможет. Решают это на уровне модели данных: очередь, пакетная запись, атомарный
UPDATE ... SET x = x + 1; - CPU-bound: вычисление внутри
async defдержит цикл, и все остальные запросы стоят. Такую работу выносят вProcessPoolExecutorчерезloop.run_in_executorили в отдельный воркер. Потоки из-за GIL для CPU не помогут, если это не free-threaded сборка Python; - блокирующие библиотеки:
requests, синхронный драйвер БД,time.sleepостанавливают весь цикл. Их либо заменяют на асинхронные (httpx,asyncpg), либо оборачивают вasyncio.to_thread.
Инструменты контроля: asyncio.Semaphore ограничивает число одновременных задач, asyncio.Lock защищает участок, где между чтением и записью есть await. Режим отладки (asyncio.run(main(), debug=True)) логирует колбэки, которые держат цикл дольше 100 мс.
Где спотыкаются
Считают, что в asyncio нет гонок, раз поток один. Код вида «прочитать, await, записать» между двумя строками отдаёт управление другой задаче, и она успевает изменить то же значение.
Запускают gather на весь список без ограничения. На тестовых ста элементах всё хорошо, на проде с сотней тысяч падает база или кончается память.
Используют threading.Lock в корутинах. Он блокирует весь поток вместе с циклом событий, и если замок держит другая корутина, сервис замирает намертво.