Асинхронность и потоки в Python: asyncio, TaskGroup и free-threading
Зачем нужна конкурентность, async/await, TaskGroup, потоки и GIL. Для опытных: TaskGroup.cancel (3.15), free-threaded сборка (3.13–3.14), субинтерпретаторы (3.14).
- Запускать сетевые задачи конкурентно
- Управлять группой задач
- Понимать, когда нужна сборка без GIL
Beginning of the lecture
Три курса валют — три секунды ожидания
- Каждый запрос к API курсов ≈ 1 с ожидания сети
- Подряд: 3.0 с, вместе: 1.0 с
- Процессор всё это время простаивает
Трекер научился переводить траты в рубли, и для этого спрашивает курсы валют у внешнего сервиса: доллар, евро, юань. Каждый ответ идёт около секунды, и программа покорно ждёт их по очереди — три секунды. При этом процессор почти всё время ничего не делает: он просто ждёт ответа по сети. Если отправить три запроса одновременно и ждать их вместе, выйдет одна секунда. Как это сделать — и почему потоки здесь не единственный ответ, — разберём сегодня.
$ uv run rates_seq.py
подряд 3.0 с
$ uv run rates_async.py
вместе 1.0 с
Ждать и считать — разные задачи
- I/O-bound: сеть, диск, база — ждём ответа
- CPU-bound: расчёты — процессор занят
- Для ожидания — asyncio или потоки, для расчётов — процессы
Сначала главное различие, без которого дальше ничего не понять. Представьте повара. Если он поставил воду и ждёт кипения, он может за это время нарезать овощи, а потом поставить духовку — один повар, много дел, потому что основное время занимает ожидание. Это задачи ввода-вывода: сеть, диск, база данных. А если он вручную месит тесто, параллельно ничего не сделаешь — руки заняты. Это вычислительные задачи. Для них нужны дополнительные повара.
async и await: функция, которая умеет ждать
- async def — корутина: вызов не выполняет, а создаёт объект
- await — «подожди, а пока займись другим»
- asyncio.run(main()) — запустить цикл событий
Асинхронный код в Python строится на двух словах. Async перед def объявляет корутину — функцию, которая умеет приостанавливаться. Await внутри неё говорит: здесь я буду ждать, а ты, цикл событий, пока займись другими задачами. Тот самый повар: поставил воду, сказал await и пошёл резать овощи. Запускается всё это функцией run. Важная деталь: внутри асинхронного кода нельзя пользоваться обычным sleep или блокирующими запросами — они остановят весь цикл.
import asyncio
async def fetch_rate(currency: str) -> float:
await asyncio.sleep(1) # имитация запроса к API
return 90.0
async def main() -> None:
for cur in ["USD", "EUR", "CNY"]:
rate = await fetch_rate(cur) # ждём по очереди
print(cur, rate)
asyncio.run(main()) # ~3 секунды
The full lecture is available with a subscription
10 more slides in the full version.
The video, all slides, notes, tests, AI tutor and interview unlock after you sign in and subscribe.
Sign in or register Get a subscriptionLecture contents
- Три курса валют — три секунды ожидания
- Ждать и считать — разные задачи
- async и await: функция, которая умеет ждать
- TaskGroup: запустить вместе и дождаться всех
- Ошибка в одной задаче отменяет остальные
- Грабли: забыли await
- GIL: почему потоки не ускоряют расчёты
- ❓ Стоп-проверка: что выбрать?
- 💡 Разбор: ждём — asyncio или потоки, считаем — процессы
- Что нового: TaskGroup.cancel (3.15)
- Что нового: Python без GIL (3.13–3.14, PEP 703 и 779)
- Что нового: субинтерпретаторы (3.14, PEP 734)
- Итог: курсы валют за секунду, расчёты — на всех ядрах
Articles on this lecture
- Asyncio in Python: async/await, TaskGroup, Threads, Multiprocessing and the GIL
- How to Download and Install Python on Windows, macOS and Linux: Step-by-Step Guide
- Strings in Python: split, replace, strip, join, Slicing and f-strings
- Python Modules and Packages: import, pip, venv and Libraries Explained Simply
- How to Learn Python from Scratch: A Step-by-Step Study Plan for Beginners
All lectures in this course
- Python 3.15 с нуля и для опытных: что нового и как учить
- Python 3.15: установка, REPL и первый скрипт за десять минут
- Переменные, числа и строки в Python: f-строки и t-строки
- Условия, циклы и match/case в Python: управляем ходом программы
- Списки, кортежи, словари и множества: frozendict в Python 3.15
- Comprehensions в Python: распаковка * и ** внутри (PEP 798)
- Функции и аннотации типов в Python: от def до sentinel
- Ошибки и исключения в Python 3.15: читаем трейсбек и подсказки «Did you mean»
- Модули, пакеты и ленивые импорты в Python 3.15 (PEP 810)
- Файлы, JSON и CSV в Python: pathlib и UTF-8 по умолчанию
- Классы и dataclasses в Python: ООП без лишнего кода
- Асинхронность и потоки в Python: asyncio, TaskGroup и free-threading
- Профилирование Python 3.15: Tachyon вместо cProfile и итоговый проект