← Назад к вопросам
За счет чего происходит переключение в asyncio?
2.3 Middle🔥 111 комментариев
#Python Core#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как происходит переключение задач в asyncio
Переключение в asyncio — это кооперативная многозадачность (cooperative multitasking), которая радикально отличается от предварительного вытеснения потоков. Давайте разберёмся, как это работает.
Основной механизм: event loop и await
import asyncio
async def task1():
print("task1 start")
await asyncio.sleep(1) # Здесь передаём управление
print("task1 end")
async def task2():
print("task2 start")
await asyncio.sleep(0.5)
print("task2 end")
async def main():
await asyncio.gather(task1(), task2())
asyncio.run(main())
Вывод:
task1 start
task2 start # task2 начал, потому что task1 отдал управление
task2 end # task2 закончился за 0.5 секунд
task1 end # task1 закончился за 1 сек
Переключение происходит в точках await
Ключевой момент: переключение случается только при await:
- Когда функция встречает
await, она говорит: "Я чего-то жду, возьми другую задачу" - Event loop берёт следующую готовую задачу
- Когда ждущая задача готова (результат приходит), она добавляется обратно в очередь
async def waiting_task():
print("Начал ждать")
result = await asyncio.sleep(1) # ПЕРЕКЛЮЧЕНИЕ! Управление идёт в event loop
print("Кончил ждать")
return result
async def working_task():
for i in range(5):
print(f"Работаю {i}")
await asyncio.sleep(0) # Явное переключение (yield)
Что внутри: event loop и очередь задач
class SimpleEventLoop:
def __init__(self):
self.tasks = [] # Очередь готовых задач
self.waiting = {} # Ожидающие задачи с timeouts
def run(self, coro):
task = coro
while True:
try:
awaitable = task.send(None)
if isinstance(awaitable, asyncio.Future):
self.waiting[awaitable] = task
ready = self._get_ready_futures()
if ready:
task = self.waiting.pop(ready[0])
continue
else:
break
except StopIteration:
break
Переключение vs многопоточность
asyncio (кооперативное):
async def io_bound():
response = await http_request()
return response
Многопоточность (вытесняющее):
def io_bound():
response = requests.get(url)
return response
Что может прерывать asyncio
Точки переключения:
await asyncio.sleep()— явное ожиданиеawait Future/await Task— ожидание асинхронной операцииawait другая_async_функция()— вызов других async функций- IO операции (если используешь асинхронные библиотеки)
async def example():
await asyncio.sleep(0)
async with aiohttp.ClientSession() as session:
data = await session.get(url)
time.sleep(1) # ПЛОХО! Блокирует весь loop
requests.get(url) # ПЛОХО! Блокирует весь loop
Event loop под капотом
На уровне ОС asyncio использует selectors (epoll на Linux, kqueue на macOS):
import selectors
sel = selectors.DefaultSelector()
while True:
ready = sel.select(timeout=0)
for key, mask in ready:
task.send(None)
Почему это эффективно
- 1 поток = N задач: одна потокозащита, нет context switching на уровне ОС
- Масштабируемость: легко обработать 10k одновременных соединений
- Детерминированность: точно знаешь, где переключения, нет race conditions
async def main():
tasks = [fetch_data(i) for i in range(10000)]
results = await asyncio.gather(*tasks)
Важные выводы
- Переключение — явное: только в точках
await - Не блокирующие операции: всегда используй асинхронные версии (aiohttp, asyncpg)
- Event loop — синглтон: весь код в одном потоке, нет гонок
- Масштабируемость: идеально для I/O bound задач