asyncio - сколько сопрограмм?

В течение нескольких дней я боролся с приложением Python, где я ожидаю найти файл или файлы в папке и выполнить итерацию по каждому файлу и каждой записи в нем, а также создать объекты для сохранения в базе данных Janusgraph. Конкретный OGM, который я использую, требует, чтобы транзакции с базой данных выполнялись асинхронно с использованием asyncio. Я прочитал много блогов, постов об asyncio и думаю, что понимаю концепцию асинхронности, ожидания, задач и т. Д. В моем приложении я определил несколько функций, которые обрабатывают различные части обработки:

  • Получает список всех доступных файлов
  • Выберите один файл для обработки
  • Перебирает выбранный файл и читает строку / запись для обработки
  • Получает запись, определяет синтаксический анализ fromin и вызывает несколько других функций, которые отвечают за создание объектов Model до того, как они будут сохранены в базе данных. Например, я создаю различные функции: пользователь, сеанс, браузер, устройство, сервер и т. Д.

Я понимаю (и могу ошибаться), что большое преимущество использования asyncio заключается в ситуациях, когда вызов функции обычно блокируется для операций ввода-вывода, транзакций базы данных, задержки в сети и т. Д.

Поэтому мой вопрос заключается в том, нужно ли мне преобразовывать все мои функции в сопрограммы и планировать выполнение цикла событий или только те, которые будут блокировать, такие как фиксация транзакции в базе данных. Я попробовал этот подход для начала, и у меня были всевозможные проблемы.

1 ответ

Решение

Поэтому мой вопрос: нужно ли мне преобразовать все мои функции в сопрограммы и расписание для выполнения цикла событий или только те, которые будут блокировать,

Возможно, вам придется конвертировать большинство из них, но преобразование должно быть в значительной степени механическим, сводящимся к изменению def в async def и добавление await при вызове других сопрограмм.

Очевидно, что вы не можете избежать преобразования тех, которые на самом деле блокируют, либо переключаясь на соответствующий asyncio API, либо используя loop.run_in_executor() для тех, у кого его нет. (Разрешение DNS было выдающимся примером последнего.)

Но тогда вам также необходимо преобразовать их вызывающие, потому что вызов сопрограммы из функции блокировки бесполезен, если функция не реализует функциональность, подобную циклу обработки событий. С другой стороны, когда сопрограмма вызывается из другой сопрограммы, все работает, потому что приостановки автоматически распространяются на вершину цепочки. Как только вся цепочка вызовов состоит из сопрограмм, высокоуровневые поступают в цикл событий, используя loop.create_task()или же loop.run_until_complete(),

Конечно, удобные функции, которые ни блокируют, ни вызывают блокирующие функции, не могут безопасно оставаться не асинхронными и вызываются либо синхронизирующим, либо асинхронным кодом без каких-либо различий.


Вышесказанное относится к asyncio, который реализует сопрограммы без стеков. Другой подход используется гринлетом, задачи которого инкапсулируют стек вызовов, что позволяет переключать их в произвольных местах в коде, который использует обычные вызовы функций. Гринлеты немного тяжелее и менее переносимы, чем сопрограммы, поэтому я сначала перешел на asyncio.

Другие вопросы по тегам