Различия между версиями 12 и 13
Версия 12 от 2022-12-24 16:09:09
Размер: 8188
Редактор: FrBrGeorge
Комментарий:
Версия 13 от 2022-12-24 16:17:37
Размер: 8188
Редактор: FrBrGeorge
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 1: Строка 1:
= 12.05 Асинхронные возможности Python = = 12.06 Асинхронные возможности Python =

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

Что нужно из теории

  • Что такое и как работает yield from

  • Как поймать значение из return значение в генераторе (res = yield from генератьор или поле исключения StopIteration)

  • async def + return ≈ генератор

  • awaityield from

Использование asyncio

  • Асинхронность в понимании asyncio:

    • Образующий цикл не виден вообще
    • Сопрограмма — это обычный async def (то есть генератор-функция, но с дополнительными свойствами)

    • Передача управления образующему циклу:
      • await asyncio.sleep(время) (в том числе 0)

      • сеть

      • А вот yield почти (или совсем) не используется

    • Сопрограммы нужно регистрировать в образующем цикле в виде заданий (.create_task())

    • Запуск образующего цикла: asyncio.run(сопрограмма)

  • B) Отличие await сопрограмма() от await asyncio.create_task(сопрограмма)

  • B) Запуск заданий с помощью .gather()

    • TODO непонятно {i} ручная барьерная синхронизация:

      • напишите функцию squarer(), возвращающую квадрат параметра, и функцию doubler(), возвращающую удвоенное значение параметра

      • по заданному списку из двух чисел [x, y] получите список [2*x^2, 2*y^2], вызвав при помощи .gather() сначала две сопрограммы squarer() на элементах этого списка, потом на элементах списка-результата две сопрограммы doubler()

      • выведите полученный в итоге список
    • // TODO ручная синхронизация: две сопрограммы возвращают .gather(), две других обрабатывают результат. Придумать правдоподобный пример

  • B) Использование событий

    • {i} цепочка событий:

      • напишите четыре функции:
        • snd() однократно отправляет событие evsnd

        • mid1() ожидает событие evsnd и после получения отправляет событие evmid1

        • mid2() аналогична mid1(), но отправляет событие evmid2

        • rcv() ожидает событий evmid1 и evmid2

        • каждая функция выводит после генерации события строку "<имя_функции>: generated <имя_события>", а после получения события - строку "<имя_функции>: received <имя_события>"

      • запустите эти четыре функции как сопрограммы
  • B) Использование очереди

    • {i} перекладывание из очереди в очередь

      • напишите три функции:
        • prod(): в цикле кладет в очередь q1 строку "value_<счетчик_цикла>", после каждой укладки ждет 1 секунду

        • mid(): в цикле ожидает получения элемента из очереди q1, кладет полученный элемент в очередь q2

        • cons(): в цикле ожидает получения элемента из очереди q2

        • цикл в функции prod() на 5 итераций, в остальных функциях - бесконечный

        • каждая функция после укладки значения выводит строку "<имя_функции>: put <значение> to <имя_очереди>", а после получения значения - строку "<имя_функции>: got <значение> from <имя_очереди>"

      • запустите эти три функции как сопрограммы
  • Начать разбор второй задачи Д/З

Д/З

  1. <!> Задача_1: Перекладывание из очереди в очередь

    • написать три сопрограммы:
      1. writer(очередь, задержка) — каждые задержка секунд помещает в очередь строку номер_задержка, где номер растёт от 0 до бесконечности. При наступлении заранее заданного события завершает работу. Работа сопрограммы начинается с задержки.

      2. stacker(очередь, стек) — бесконечно перекладывает содержимое очереди в стек. При наступлении заранее заданного события завершает работу.

      3. reader(стек, количество, задержка) — снимает и выводит значение со стека каждые задержка секунд. После вывода количество значений посылает то самое событие и завершает работу. Работа сопрограммы начинается с задержки.

    • Считать через запятую четыре значения: задержка1, задержка2, задержка3, количество. Это, соответственно, задержки двух writer-ов и reader-а, и количество чтений.

      • In:

         2,3,1,10
      • Out:

        0_2
        0_3
        1_2
        1_3
        2_2
        3_2
        2_3
        4_2
        3_3
        5_2
  2. <!> Задача_2: сортировка слиянием

    • написать сопрограмму «слияние двух соседних упорядоченных отрезков» merge(A, B, start, middle, finish, event_in1, event_in2, event_out)

      • ждёт наступления обоих _in-событий (признак того, что отрезки отсортированы)

      • объединяет два упорядоченных отрезка A[start…middle) и A[middle…finish) в один B[start…finish)

      • посылает событие event_out

    • Написать сопрограмму mtasks(A), в которой

      • Запланировать сначала объединение отрезков A единичной длины, помещая их в B, потом — отрезков B длины 2, помещая в A, потом длины 4 и т. д.

      • Должно получиться от N - 1 до N + log₂N заданий, зависимость между которыми задана событиями

      • Возвращается список заданий task, пригодный для одновременного запуска посредством asyncio.gather(*tasks)

    • In:

         1 import asyncio
         2 
         3 async def main(A):
         4     tasks, B = await mtasks(A)
         5     print(len(tasks))
         6     random.shuffle(tasks)
         7     await asyncio.gather(*tasks)
         8     return B
         9 
        10 
        11 random.seed(1337)
        12 A = random.choices(range(10), k=33)
        13 B = asyncio.run(main(A))
        14 print(*A)
        15 print(*B)
        16 print(B == sorted(A))
      
    • Out

      37
      6 5 3 5 1 8 3 7 9 3 9 2 6 9 1 8 3 0 6 3 4 8 8 6 8 5 4 9 7 3 6 9 4
      0 1 1 2 3 3 3 3 3 3 4 4 4 5 5 5 6 6 6 6 6 7 7 8 8 8 8 8 9 9 9 9 9
      True

LecturesCMC/PythonIntro2022/Prac/13_Async (последним исправлял пользователь FrBrGeorge 2022-12-24 16:17:37)