Когда бы не использование asyncio имеет смысл? - PullRequest
1 голос
/ 22 апреля 2019

В каких случаях один поток или исполнители (использующие потоки) превышают asyncio?

По мере того, как мой опыт использования Python (CPython) прогрессировал, он сосредоточился вокруг оптимизации сценариев на работе для выполнения какой-либо формы вызова веб-сервисов массовым способом и обработки ответов. Однако, после нескольких поколений сценариев, я задаюсь вопросом, почему бы мне не использовать последние версии?

Позвольте мне предоставить контекст ниже ...

Проблема: Запрос N файлов с сервера A на клиент B, обработка и сохранение на диск.

  1. Последовательная

    • Создание контейнера запросов, отправка одного запроса, обработка ответа, повторение до завершения
    • Вероятно, считается подходом "стандарт / новичок", поскольку он интуитивно достигнут
  2. многопоточных

    • Снова построить контейнер, но одновременно отправлять несколько запросов
    • Использование семафора для ограничения активных соединений
    • Использование очереди для обмена между работниками и получения ответов дампа
    • Пусть основной поток обрабатывает ответы
    • В сущности, работники работают по принципу "забей и забудь", и основные циклы выполняются в цикле проверки очереди данных
    • Обеспечивает отделение проблем от основного, который обрабатывает только данные
  3. ThreadPoolExecutor

    • По существу, как решение 2, за исключением гораздо меньшего количества строк кода
    • Обоснование: «Я хотел бы иметь возможность обработать ответ, как только он станет доступен»
    • Явное создание экземпляров очереди и семафора не требуется
    • Если не ошибаюсь, использование структуры Queue and Thread используется в as_completed()
    • В значительной степени обрисовано в общих чертах здесь
  4. asyncio

    • Введите здесь серьезную путаницу, но концепция в основном понятна
    • Работает в одном потоке в отличие от решений 2 и 3
    • Ближе (очень) к решению 3 в реализации, кроме записи на диск
    • Требуется использование компонента Solution 3 для сохранения на диск с помощью run_in_executor()

Таким образом, мы подошли к моей нынешней дилемме: с чего бы мне не хотеть использовать asyncio для работы, связанной с вводом / выводом?

Асинхронное программирование является концепцией, очень похожей на ООП, и в документах по Решению 3 даже говорится, что «асинхронное выполнение может выполняться с потоками». Но если я могу добиться асинхронного выполнения в одном потоке (исключая дополнительный поток для блокировки ввода-вывода на диск), зачем мне когда-либо использовать Решения 1-3?

Я знаю, что с учетом GIL многопоточность CPython является неоптимальной; Тем не менее, я не вижу причин, по которым кто-либо мог бы использовать потоки или исполнителей. Я провел немало Googling, чтобы посмотреть, смогу ли я найти хорошую статью, в которой говорится, почему кто-то предпочел бы их использовать, но я нашел только статьи, в которых говорится, почему потоки (и впоследствии исполнители, использующие потоки) плохи: переключение контекста ( GIL / OS), условия гонки, истощение ресурсов и т.д ...

Поскольку CPython не использует потоки для использования преимуществ многоядерных процессоров (я полагаю, это библиотека multiprocessing), потоки не используются для сложных вычислительных задач; таким образом, ограничивая их операциями, связанными с вводом / выводом, для увеличения производительности. Это, однако, не дает мне достаточных оснований понять, почему потоки или исполнители будут использоваться вместо asyncio.

Если вы можете сделать все это в одном потоке (может быть, 2-3), зачем продолжать вводить накладные расходы на создание, управление и уничтожение потоков (как явным образом, так и через пул / исполнителя)?

1 Ответ

0 голосов
/ 22 апреля 2019

Я думаю, что решение между многопоточностью и asyncio действительно будет в том, какой тип многозадачности вам нужен.Если в программе все находится под вашим контролем, асинхронная / многопроцессорная обработка может быть правильным выбором все время.Однако, возможно, вы хотите начать задачу, в которой многозадачность с предварительным вытеснением является правильным выборомНапример, вы запускаете задачу в сторонней библиотеке.Основной причиной использования потоков будет то, что библиотека не поддерживает asyncio.Однако, даже если он поддерживает asyncio, возможно, вы не хотите доверять этой библиотеке, чтобы контролировать ваши задачи так часто, как вы хотите.Затем вы могли бы начать новый поток с другим циклом событий asyncio, который запускает этот код.

Поэтому я думаю, что реальный вопрос заключается в следующем: когда использовать кооператив и когда использовать многозадачность с предварительным вытеснением.

...