Асинхронные возможности Python

Асинхронность:

Модель

<!> Предполагается, что весь предлагаемый код вы запускаете и смотрите на результат; без этого понять намного сложнее, if even possible ☺

Асинхронность как произвольное исполнение частей кода между yield-ами

Можно и дальше усложнять, но и так уже непросто!

Ещё модели

Ещё раз: асинхронность — это не параллелизм! Все фрагменты выполняются последовательно в один поток.

Синтаксис Async

<!> Если до этого момента не стало понятно:

Перепишем предыдущий пример на async

Примечание: @types.coroutine — низкоуровневая сопрограмма, которая может делать и return значение, и yield, то есть напрямую обращаться к образующему циклу. Встречается редко.

   1 from random import randint
   2 from string import ascii_uppercase
   3 from types import coroutine
   4 from collections import deque
   5 
   6 @coroutine
   7 def subr():
   8     return (yield int) * (yield str)
   9 
  10 async def task(num):
  11     res = ""
  12     for i in range(num):
  13         res += await subr()
  14     return res
  15 
  16 def loop(*tasks):
  17     queue, result = deque((task, None) for task in tasks), []
  18     print("Start:", *queue, sep="\n\t")
  19     idx = -1
  20     while queue:
  21         task, request = queue.popleft()
  22         if request is int:
  23             data = randint(1, 4)
  24         elif request is str:
  25             data = ascii_uppercase[idx := idx + 1]
  26         else:
  27             data = request
  28         try:
  29             request = task.send(data)
  30         except StopIteration as ret:
  31             result.append((task, ret.value))
  32             task.close()
  33         else:
  34             queue.append((task, request))
  35     return result
  36 
  37 print("Done:", *loop(task(10), task(3), task(5)), sep="\n\t")

<!> Формально говоря, awaitable object — это просто объект с методом .__await__(), возвращающим итератор. Однако логика работы этого метода диктуется управляющим циклом, это вам не .__call__ ☺. Вот пример реализации логики Future для управляющего цикла asyncio.

Asyncio

Немного истории:

Базовая документация

Основные понятия:

Asyncio — это:

Высокоуровневое API (введение)

Синхронизация

И толстый-толстый слой шоколада!

Python3.14+: Интроспекция запущенной asyncio-программы

Д/З

  1. Попробовать прочитать всю документацию и прощёлкать всё, до чего дотянетесь.

  2. EJudge: TwoWay 'Тамбур'

    Написать класс Portal, который работает так же, как Barrier, однако дополнительно имеет property .topic. Этот дескриптор по умолчанию равен None, однако вызов .wait(топик) с не-None параметром его меняет на топик. Главное свойство Portal состоит в том, что к моменту «прохождения портала» любым его клиентом значение topic должно быть равно заданному.

    • Предполагается, что из клиентов только один задаёт топик, остальные не меняют его (например, передают None)

    Input:

       1 async def task(p, topic=None):
       2     await p.wait(topic)
       3     print(p.topic)
       4 
       5 async def runner(N):
       6     p = Portal(N)
       7     async with asyncio.TaskGroup() as tg:
       8         for j in range(3):
       9             for i in range(N):
      10                 tg.create_task(task(p, f"FLAG{j}" if i == N // 2 else None))
      11 
      12 asyncio.run(runner(5))
    
    Output:

    FLAG0
    FLAG0
    FLAG0
    FLAG0
    FLAG0
    FLAG1
    FLAG1
    FLAG1
    FLAG1
    FLAG1
    FLAG2
    FLAG2
    FLAG2
    FLAG2
    FLAG2

LecturesCMC/PythonIntro2025/13_Async (последним исправлял пользователь FrBrGeorge 2025-12-01 18:26:46)