1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
### Под капотом asyncio: принципы работы и ключевые концепции ###
**Библиотека asyncio предоставляет полный набор инструментов для организации параллельного выполнения кода в Python с использованием концепции асинхронности. Но как на самом деле работает asyncio? Давайте разберемся в ключевых принципах и понятиях.
Библиотека asyncio предоставляет полный набор инструментов для организации параллельного выполнения кода в Python с использованием концепции асинхронности. Но как на самом деле работает asyncio? Давайте разберемся в ключевых принципах и понятиях.
Этот материал взят из нашей субботней [email-рассылки](https://proglib.io/w/edaee37b), посвященной Python. Подпишитесь, чтобы быть в числе первых, кто получит дайджест.
**Asyncio** – библиотека, которая предоставляет инфраструктуру для написания параллельного кода с использованием концепции асинхронного программирования. Она позволяет эффективно обрабатывать многочисленные задачи ввода-вывода (например, сетевые операции или чтение/запись из файлов) без необходимости создавать множество потоков или процессов. Ключевые концепции asyncio:
**Событийный цикл (Event Loop)**
- Это ядро **asyncio**, которое отвечает за планирование и выполнение задач (корутин).
- Реализован на языке **C** для максимальной эффективности.
- Работает по принципу **однопоточной многозадачности** (single-threaded concurrency).
- Постоянно опрашивает очередь событий и выполняет соответствующие задачи.
**Корутины (Coroutines)**
- Это специальные функции, выполнение которых можно приостанавливать на определенных точках с помощью ключевого слова `await`.
- Используются для написания асинхронного, событийно-ориентированного кода.
- Могут ждать асинхронные события (ввод-вывод, сигналы или другие корутины).
- Определяются с помощью `async def`.
**Задачи (Tasks)**
- Представляют собой обертки над корутинами для их планирования и выполнения.
- Создаются с помощью `asyncio.create_task(coroutine)` или `asyncio.ensure_future(coroutine)`.
- Задачи можно отменять, объединять с другими задачами и отслеживать на предмет исключений.
- Задачи добавляются в событийный цикл и выполняются в порядке их готовности.
**Будущие объекты (Futures)**
- Представляют результат асинхронной операции, который может быть доступен позже.
- Задачи наследуются от класса `Future` и добавляют собственные методы управления.
- Могут быть использованы для координации между разными частями программы.
**Транспорты и протоколы**
- **Транспорты** (sockets) отвечают за **передачу** данных по сети.
- **Протоколы** определяют, как **интерпретировать** эти данные (HTTP, WebSocket и др.).
- asyncio предоставляет высокоуровневые абстракции для работы с транспортами и протоколами.
**Другие инструменты**
- Пулы для управления ограниченными ресурсами (потоки, подпроцессы).
- Синхронизаторы для координации между корутинами (**Lock****,** **Event**, **Condition)**.
- Очереди для безопасной передачи данных между корутинами.
- Сигналы для обработки внешних событий (UNIX-сигналы).
**Вот как работает asyncio:**
- Создается **событийный цикл**, который будет управлять выполнением задач.
- Определяются корутины, которые выполняют некоторую работу и могут приостанавливаться на операциях ввода-вывода с помощью ключевого слова `await`.
- Корутины оборачиваются в задачи с помощью функции `asyncio.create_task()`.
- Задачи планируются для выполнения в событийном цикле с помощью функции `asyncio.run()` или добавляются в цикл вручную.
- Событийный цикл начинает выполнять задачи по очереди. Когда задача достигает операции ввода-вывода (например, `await asyncio.sleep()`), она приостанавливается, и событийный цикл переключается на другую задачу.
- Когда операция ввода-вывода завершается, задача возобновляется и продолжает выполнение до следующей операции ввода-вывода или завершения.
Статья по теме
[Упрощаем разработку: асинхронные функции Python](https://proglib.io/p/uproshchaem-razrabotku-asinhronnye-funkcii-python-2019-10-06)
Так asyncio позволяет эффективно использовать **один** поток для выполнения **множества** задач с операциями ввода-вывода, избегая блокировки и простоев из-за ожидания завершения этих операций.
Важно отметить, что asyncio оптимален **именно для I/O-bound задач** (сеть, файловые операции и т. д.), и **не подходит для CPU-bound операций** (вычислительно-интенсивных задач), поскольку в этом случае он не сможет переключаться на другие задачи во время блокировки. Для такого рода задач лучше использовать многопоточность или многопроцессорность. Подробнее о принципах работы asyncio – в [этой статье](https://jacobpadilla.com/articles/recreating-asyncio) и [в этом видео](https://www.youtube.com/watch?v=RIVcqT2OGPA)[](http://savefrom.net/?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DRIVcqT2OGPA&utm_source=userjs-safari&utm_medium=extensions&utm_campaign=link_modifier "Получи прямую ссылку").
=======================================
🚀 **asyncio** – это библиотека для асинхронного программирования, позволяющая писать код, выполняющий несколько задач параллельно (без потоков и процессов).
✅ async def – объявление асинхронной функции (корутина).
✅ await – приостанавливает выполнение до завершения асинхронной операции.
✅ asyncio.run() – запускает главную асинхронную функцию.
✅ asyncio.create_task() – запускает корутину в фоновом режиме.
✅ asyncio.sleep() – имитирует задержку без блокировки других задач.
```python
import asyncio
async def say_hello():
await asyncio.sleep(1) # Имитация задержки
print("Привет, мир!")
async def main():
task1 = asyncio.create_task(say_hello())
task2 = asyncio.create_task(say_hello())
await task1
await task2
asyncio.run(main()) # Запуск асинхронной программы
```
**Выведет с задержкой 1 секунду:**
Привет, мир!
Привет, мир!
Обе задачи выполняются **параллельно**, а не последовательно!
📌 Когда использовать asyncio?
✅ Запросы в интернет (aiohttp).
✅ Работа с базами данных (aiomysql, asyncpg).
✅ Асинхронное чтение файлов/сокетов.
✅ Многозадачность без потоков и процессов.
#======================================
**Вот как работает asyncio — без магии:**
📌 1. **Event Loop — это «планировщик», который гоняет корутины. Он сам не создаёт потоки. Он ждёт, когда одна задача «освободит» CPU, и сразу берётся за другую.**
```python
import asyncio
async def main():
print("Start")
await asyncio.sleep(1)
print("End")
asyncio.run(main())
```
Здесь await asyncio.sleep(1) — не блокирует поток. Это ***yield*,** который говорит: "я пока жду, можешь заняться чем-то ещё".
📌 2. **Ты не должен использовать time.sleep() в async-коде. Это блокирует весь event loop. Заменяй на await asyncio.sleep().**
📌 3. **Любая синхронная тяжёлая операция — угроза. Например, чтение файла или SQL-запрос через обычный драйвер.**
✔️ Как решить:
👍 Используй aiofiles для файлов.
👍 Используй async-драйверы (asyncpg, aiomysql, httpx и т.д.)
👍 Если нет async-аналога — выноси в ThreadPoolExecutor.
```python
from concurrent.futures import ThreadPoolExecutor
import asyncio
def blocking():
...
async def main():
loop = asyncio.get_running_loop()
await loop.run_in_executor(None, blocking)
```
📌 4.**Не забывай про семафоры, если запускаешь 1000 задач одновременно. async не значит всё сразу.**
```python
sem = asyncio.Semaphore(100)
async def safe_task():
async with sem:
await do_something()
```
🧠 asyncio — это не про параллельность. Это про эффективное переключение задач, которые ждут чего-то: ответа сервера, данных, паузы.
🗣 **Применяется в: Web-серверах (FastAPI, aiohttp), парсинге, бэкенде с I/O, высоконагруженных API.**
|