⇤ ← Версия 1 от 2020-12-10 13:39:35
6105
Комментарий:
|
6244
|
Удаления помечены так. | Добавления помечены так. |
Строка 35: | Строка 35: |
1. Эшелонируем сортировку с помощью задания тайм-аута (в пятом параметре будем передавать номер ''эшелона'' `n` и ждать `n/c` секунд) и общего `create_task() / gather()` | 1. {i} Эшелонируем сортировку с помощью задания тайм-аута (в пятом параметре будем передавать номер ''эшелона'' `n` и ждать `n/c` секунд) и общего `create_task() / gather()` |
Строка 38: | Строка 38: |
* Пример | |
Строка 44: | Строка 45: |
1. Задача_1: переписать сортировку с использованием `future` для синхронизации (например, `слияние_4_8` должно дожидаться двух фьюч — `фьюча_4_6` и `фьюча_6_8`) 1. Задача_2: (на дом). Переписать сортировку под произвольный (не слишком большой) объём данных |
1. <!> Задача_1: переписать сортировку с использованием `future` для синхронизации (например, `слияние_4_8` должно дожидаться двух фьюч — `фьюча_4_6` и `фьюча_6_8`) '''TODO''' формулировка и формат 1. <!>Задача_2: (на дом). Переписать сортировку под произвольный (не слишком большой) объём данных '''TODO''' формулировка и формат |
12.10 Асинхронные возможности Python
Краткий пересказ теории
- Асинхронная парадигма (вариант реализации): образующий цикл, события и обратные вызовы
- Python: вместо обратных вызовов — повторный вход в генератор (см. параметрические генераторы), образующий цикл программируйте сами!
- Асинхронность
последовательное выполнение ограниченных yield фрагментов кода различных генераторов в порядке, который определяет программист.
Образующий цикл согласно какой-то логике выбирает сопрограмма и даёт команду отчёт = сопрограмма.send(команда) для выполнения очередного фрагмента
Сопрограмма получает команду с помощью команда = yield(предыдущий отчёт), анализирует её и выполняет следующий фрагмент кода
В действительности:
Образующий цикл (со своей логикой) уже реализован кем-то
Инструменты управления этой логикой реализованы тем же автором
Поэтому пользователи этой действительности не пишут .send() или явный yield
Основная конструкция — результат = yield from генератор, который возвращает то, что у генератора написано в return
Синтаксический сахар и асинхронный протокол:
Вместо генератора пишем сопрограму с помощью async def. Это тот же генератор, но в нём нельзя пользоваться yield
Вместо yield from пишем результат = await сопрограмма (различие в более удобном синтаксисе и в проверке того, сопрограмма с помощью async def)
- Всё остальное берёт на себя разработчик инструментария:
- Образующий цикл
Инструменты управления образующим циклом, которым разрешено делать прямой yield в него
- …
Asyncio
- Напишем программу сортировки слиянием массива из 16 элементов
Все 15 слияний делать руками с помощью вызова функции слить(начало1, конец1, начало2, конец2)
- Для слияния используется общий глобальный второй массив
Перетащим их в структуру asyncio.run()/await
Для наглядности снабдим каждый шаг сортировки await asyncio.sleep(0.1) и выводом номера «задания» (передадим его в качестве пятого параметра)
Разницы никакой, всё равно запуск последовательный
Для придания асинхронности надо писать свой mainloop. Но он уже есть — это asyncio.run(). Не надо писать свой mainloop, надо пользоваться asyncio.create_task() и asyncio.gather(все 15 слияний)
- Получается какая-то каша вместо сортировки, почему?
Эшелонируем сортировку с помощью нескольких фрагментов вида create_task()… + gather(все_таски_из фрагмента) (палево — их пять)
Эшелонируем сортировку с помощью задания тайм-аута (в пятом параметре будем передавать номер эшелона n и ждать n/c секунд) и общего create_task() / gather()
- Чем этот подход плох?
- Фьюча (future).
- Пример
- Алгоритм работы
- Awaitable-объект, у которого есть творец и адресат, оба имеют к нему доступ.
Адресат делает результат = await фьюча, и уходит в mainloop.
Творец, когда ему заблагорассудится, с помощью фьюча.set_result(что-то) объявляет mainloop-у, что будущее наступило с результатом что-то
- Тогда mainloop, наконец, активизирует эту фьючу и адресат просыпается
- В действительности, фьюча — это просто генератор на два шага: будушее не наступило / yield / будущее наступило, в котором есть два дополнительных поля (объявление о том, что оно наступило и передаваемое значение)
Задача_1: переписать сортировку с использованием future для синхронизации (например, слияние_4_8 должно дожидаться двух фьюч — фьюча_4_6 и фьюча_6_8)
TODO формулировка и формат
<!>Задача_2: (на дом). Переписать сортировку под произвольный (не слишком большой) объём данных
TODO формулировка и формат