Я шёл на интервью после пары постов про asyncio и думал, что «понял async». Первый вопрос — «в чём разница async и threading» — я ещё с грехом пополам проговорил. Потом спросили про GIL, event loop, и почему для моего «сложного расчёта» async не поможет. И я поплыл. Даже честное «честно, до конца не разобрался» прозвучало как признание провала. Обратно ехал злой и раздавленный.

Нейтрально и без паники. Разложить два мира:

  • threading / многопоточность — больше «рабочих» в одном процессе; для задач, ждущих чего-то (опять же I/O) или вычислительных — но в CPython с GIL тяжёлые счётные поток в одно время не ускорить;
  • async/await — один поток, который во время ожидания (сеть, база) переключается на другие задачи, не простаивая. Идеально для многих I/O-запросов, и не ускоряет чистое вычисление.
import asyncio, threading, time

def cpu_bound(n):           # считает — поток на GIL не поможет
    return sum(i * i for i in range(n))

async def io_like(t):       # ждёт — async выигрывает
    await asyncio.sleep(t)
    return "ok"

# игрушечная иллюстрация разницы «ожидания» vs «счёта»

Стыд прошёл, когда я прочитал дальше и понял: я путал «когда использовать». Теперь держу правило: I/O → async подходит, сложный расчёт → потоки/процессы или подумать про алгоритм. Провал стал самым полезным уроком месяца — заставил закрыть дыру, о которой я и не подозревал. Хочется верить, что следующий разговор про это будет спокойнее.