← Назад к вопросам

Может ли одна корутина отдать управление другой корутине?

2.7 Senior🔥 201 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Может ли одна корутина отдать управление другой корутине

Да, может! Это основная идея asyncio. Корутины передают управление друг другу через await.

Как корутины передают управление

await - это точка, где текущая корутина временно останавливается и позволяет другой работать.

import asyncio

async def coroutine_a():
    print("A: начало")
    await asyncio.sleep(1)
    print("A: конец")

async def coroutine_b():
    print("B: начало")
    await asyncio.sleep(1)
    print("B: конец")

async def main():
    await asyncio.gather(
        coroutine_a(),
        coroutine_b()
    )

asyncio.run(main())

Механизм: await передаёт управление

Когда ты пишешь await что-то, ты говоришь:

  1. Я приостанавливаюсь здесь
  2. Другие корутины могут работать
  3. Когда что-то будет готово, разбуди меня
async def fetch_user(user_id):
    response = await http_client.get(f'/users/{user_id}')
    return response.json()

async def main():
    results = await asyncio.gather(
        fetch_user(1),
        fetch_user(2),
        fetch_user(3)
    )

Явное отдание управления

async def task_a():
    for i in range(3):
        print(f"A: {i}")
        await asyncio.sleep(0)

async def task_b():
    for i in range(3):
        print(f"B: {i}")
        await asyncio.sleep(0)

asyncio.run(asyncio.gather(task_a(), task_b()))

Что происходит БЕЗ await?

Если не используешь await, управление НЕ передаётся!

async def task_a():
    for i in range(3):
        print(f"A: {i}")

async def task_b():
    for i in range(3):
        print(f"B: {i}")

asyncio.run(asyncio.gather(task_a(), task_b()))

# task_a полностью закончилась перед task_b!

Параллельное выполнение vs последовательное

import asyncio
import time

async def slow_task(name, duration):
    print(f"{name} начал ({duration}s)")
    await asyncio.sleep(duration)
    print(f"{name} готов")

async def sequential():
    start = time.time()
    await slow_task("Task1", 2)
    await slow_task("Task2", 2)
    await slow_task("Task3", 2)
    print(f"Время: {time.time() - start:.1f}s")

async def parallel():
    start = time.time()
    await asyncio.gather(
        slow_task("Task1", 2),
        slow_task("Task2", 2),
        slow_task("Task3", 2)
    )
    print(f"Время: {time.time() - start:.1f}s")

Event Loop - дирижер оркестра

Event loop управляет всеми корутинами:

  1. Запускает task1
  2. task1 встречает await -> приостанавливается
  3. Запускает task2
  4. task2 встречает await -> приостанавливается
  5. Ждёт когда обе готовы
  6. Возобновляет обе
import asyncio

async def task1():
    print("Task1: работаю")
    await asyncio.sleep(0.1)
    print("Task1: готов")

async def task2():
    print("Task2: работаю")
    await asyncio.sleep(0.1)
    print("Task2: готов")

async def main():
    await asyncio.gather(task1(), task2())

asyncio.run(main())

Проблема: blocking без await

import asyncio
import time

async def blocking_task():
    print("Начало")
    time.sleep(2)  # БЛОКИРУЕТ весь event loop!
    print("Конец")

async def other_task():
    for i in range(3):
        print(f"Other: {i}")
        await asyncio.sleep(0.1)

async def main():
    await asyncio.gather(blocking_task(), other_task())

Other не мог работать пока blocking_task спал!

Решение: executor для blocking кода

import asyncio
import time

async def blocking_task():
    print("Начало")
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, time.sleep, 2)
    print("Конец")

async def other_task():
    for i in range(3):
        print(f"Other: {i}")
        await asyncio.sleep(0.1)

async def main():
    await asyncio.gather(blocking_task(), other_task())

asyncio.run(main())

Контекстные переменники

import asyncio
from contextvars import ContextVar

request_id = ContextVar('request_id', default='no-id')

async def process(name):
    rid = request_id.get()
    print(f"{name} начал с request_id={rid}")
    await asyncio.sleep(0.1)
    print(f"{name} завершился")

async def main():
    token1 = request_id.set('req-1')
    task1 = asyncio.create_task(process('Task1'))
    
    token2 = request_id.set('req-2')
    task2 = asyncio.create_task(process('Task2'))
    
    await asyncio.gather(task1, task2)

asyncio.run(main())

Каждая корутина имеет свой контекст!

Ожидание завершения конкретной корутины

async def producer():
    for i in range(3):
        print(f"Producing {i}")
        await asyncio.sleep(0.1)

async def consumer():
    print("Consuming")
    await asyncio.sleep(0.5)

async def main():
    await asyncio.gather(producer(), consumer())

Заключение

  • Корутины передают управление через await
  • Event loop управляет всеми корутинами
  • asyncio.gather() запускает параллельно
  • Без await нет передачи управления
  • Используй async/await для масштабируемых I/O приложений

Основная идея: одна корутина ждёт (await), другие продолжают работать. Это однопоточная модель параллелизма, но очень эффективная для I/O операций.

Может ли одна корутина отдать управление другой корутине? | PrepBro