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

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

Модель

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

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

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

Ещё модели

Синтаксис Async

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

Asyncio

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

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

Asyncio — это:

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

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

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

Д/З

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

  2. EJudge: MobQueue 'В очередь!'

    (Фактически это упражнение). Написать две сопрограммы — sender(queue, pattern) и reader(queue, number), которые работают так:

    • sender(queue, pattern) складывает в объект queue типа asyncio-queue.html все строки из последовательности pattern (гарантируется, что pattern — это последовательность строк). Когда последовательность заканчивается, sender ставит в очередь None и завершается

    • reader(queue, number) читает из очереди все объекты до тех пор, пока там не встретится number объектов None (это будет означать, что все sender-ы отработали). reader() должен возвращать объект типа Counter, в котором подсчитано количество вхождений всех строк из очереди (но не None).

    Input:

       1 import asyncio
       2 import string
       3 
       4 async def main(n):
       5     queue = asyncio.Queue(4)
       6     alp = string.ascii_lowercase
       7     senders = [sender(queue, alp[len(alp) * i // n: len(alp) * (i + 1) // n]) for i in range(n)]
       8     res = await asyncio.gather(reader(queue, n), *senders)
       9     print(", ".join(f"{key}:{val}" for key, val in sorted(res[0].items())))
      10 asyncio.run(main(6))
    
    Output:

    a:1, b:1, c:1, d:1, e:1, f:1, g:1, h:1, i:1, j:1, k:1, l:1, m:1, n:1, o:1, p:1, q:1, r:1, s:1, t:1, u:1, v:1, w:1, x:1, y:1, z:1
  3. EJudge: BarrierSync 'К барьеру!'

    Написать сопрограмму serial(number, barrier), принимающую два параметра — некоторый номер number и объект barrier типа Barrier.

    • В тестах создаётся один барьер bar определённого размера num и запускается num заданий видаserial(i, bar), где i — некоторое произвольное целое

    • Каждая сопрограмма должна однократно выводить number.

    • Задание serial(m, b) выводит m раньше, чем serial(n, b) выводит n, если m < n

      • Таким образом в результате номера выводятся в порядке неубывания.
    Input:

       1 from random import shuffle
       2 
       3 async def main(num):
       4     bar = asyncio.Barrier(num)
       5     tasks = [serial(i * 2 % num, bar) for i in range(num)]
       6     shuffle(tasks)
       7     await asyncio.gather(*tasks)
       8 
       9 asyncio.run(main(10)) 
    
    Output:

    0
    0
    2
    2
    4
    4
    6
    6
    8
    8
  4. EJudge: FilterQueue 'Вас здесь не стояло!'

    Напишите класс FilterQueue со следующими свойствами:

    • Это потомок asyncio.Queue

    • В экземпляре класса атрибут очередь.window содержит первый элемент очереди, или None, если очередь пуста (просмотр очередь.window не влияет на состояние очереди)

    • С помощью операции фильтр in очередь можно определить, присутствуют ли в очереди такие элементы, что выражение фильтр(элемент) истинно

    • Метод .later() синхронно переставляет первый элемент очереди в её конец, или вызывает исключение asyncio.QueueEmpty, если очередь пуста

    • Метод .get() содержит необязательный параметр фильтр. Вызов очередь.get(фильтр) работает так:

      • Если в очереди нет элементов, на которых фильтр(элемент) истинно, работает как обычный .get().

      • Если в очереди есть элементы, на которых фильтр(элемент) истинно, переставляет первый элемент очереди в её конец до тех пор, пока фильтр(элемент) не истинно, а затем выполняет обычный .get().

    • Разрешается воспользоваться внутренним представлением Queue; код Queue можно посмотреть тут

    Input:

       1 async def putter(n, queue):
       2     for i in range(n):
       3         await queue.put(i)
       4 
       5 async def getter(n, queue, filter):
       6     for i in range(n):
       7         await asyncio.sleep(0.1)
       8         yield await queue.get(filter)
       9 
      10 async def main():
      11     queue = FilterQueue(10)
      12     asyncio.create_task(putter(20, queue))
      13     async for res in getter(20, queue, lambda n: n % 2):
      14         print(res)
      15 
      16 asyncio.run(main())
    
    Output:

    1
    3
    5
    7
    9
    11
    13
    15
    17
    4
    19
    12
    6
    16
    8
    14
    0
    10
    2
    18

LecturesCMC/PythonIntro2023/13_Async (последним исправлял пользователь Vladimir 2024-01-15 16:20:06)