Pull to refresh

Comments 13

По поводу "Ограничений". Не обязательно искать асинхронные библиотеки. Можно убрать синхронные вызовы в ThreadPoolExecutor, особенно хорошо работает в случаях IO

Я с вами полностью согласен что он очень хорош для IO.

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

Спасибо за комментарий.Я добавлю ваше описание в статью.

Асинхронность по определению замедляет сложные вычисления. Например, для asyncio и trio в силу цикла событий и переключений контекста.

А почему? Если у вас сложные вычисления, то у вас скорее всего не будет переключения контекста, наоборот должно работать "быстрее". Другой вопрос, что никакой конкурентности при этом не будет

В моём представлении, "асинхронное программирование" -- синоним "конкурентной многозадачности". Поэтому я не совсем понимаю, что Вы имеете ввиду.

Возьмём синтетический пример ограниченной ЦПУ (CPU bound) задачи. Например, вычисление 10.000 списков со списками квадратов целых чисел от 0 до 10.

Пример реализации с синхронным (последовательным) выполнением:

import timeit

def foo():
    return [i ** i for i in range(10)]

def bar():
    result = [foo() for i in range(10_000)]
    print(result)

print(timeit.timeit(
    "bar()",
    setup="from __main__ import bar",
    number=1,
))

Пример реализации потенциально поддерживающей асинхронное (конкурентное) выполнение (скажем, если бы работа корутины foo() была связана с независящими от ЦПУ задержками):

import asyncio
import timeit

async def foo():
    return [i ** i for i in range(10)]

async def bar():
    result = await asyncio.gather(*[foo() for i in range(10_000)])
    print(result)

print(timeit.timeit(
    "asyncio.run(bar())",
    setup="from __main__ import bar; import asyncio",
    number=1,
))

Инициализация цикла событий под капотом asyncio во втором примере не должна привносить больших накладных расходов на фоне 10.000 корутин.

Запускаем каждый файл с примером 3 раза (используется CPython 3.10.8).

Синхронный пример:

0.04563898383639753
0.04258333402685821
0.048088460927829146

Асинхронный пример:

0.13237898400984704
0.1389962760731578
0.11636401992291212

Неожиданно, конкурентный запуск 10.000 корутин в 2-3 раза превышает по времени 10.000 последовательных вызовов одной функции.

А сколько памяти эти два процесса запрашивают у операционной системы?

Синхронный пример:

Maximum resident set size (kbytes): 13388
Maximum resident set size (kbytes): 13380
Maximum resident set size (kbytes): 13548

Асинхронный пример:

Maximum resident set size (kbytes): 31436
Maximum resident set size (kbytes): 31224
Maximum resident set size (kbytes): 31164

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

Конечно, можно было бы избавиться от asyncio.gather() и последовательно делать await корутины в цикле for... Вот только, по моему скромному мнению, последовательное выполнение -- это синоним синхронного выполнения.

И если кто-то думает, что асинхронность -- это обмазать свои функции и вызовы ключевыми словами async/await и сунуть свой main() в asyncio.run(), значит у меня с этим человеком есть небольшое расхождение в терминах.

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

В этом посте я подробно расскажу о важной фиче, которая появилась в Python 3.5 — асинхронности.

Twisted -- 2002 год.
Tornado -- 2009 год.
Python 3.5 -- 2015 год.

Пост будет полезен новичкам для понимания основ асинхронности и, может, даже опытным разработчикам в поиске новых идей и подходов.

Поверим на слово, вдруг и правда полезен.

Асинхронность — это парадигма программирования, которая позволяет выполнять несколько задач одновременно, не дожидаясь завершения каждой из них;

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

важный инструмент для решения проблем с производительностью в веб-приложениях и серверных технологиях.

Тоже скорее вреден. Слишком общая формулировка опускает различия между:

  • задачами упирающимися в ЦПУ

  • задачами упирающимися в дисковый ввод-вывод

  • задачами упирающимися в сетевой ввод-вывод

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

Ой, всё...

Twisted -- 2002 год.
Tornado -- 2009 год.
Python 3.5 -- 2015 год.

Ничего не могу сказать про Twisted, возможно там просто использовалось несколько потоков, а потом перешли на asyncio. А вообще идея asyncio была предоставлена Дэвидом Бизли в 2009 на Pycon. Изначально вместо ключевых слов async/await использовались итераторы

Я лучше промолчу и Вам советую. Похоже именно за комментарий выше (оценки +6 -1) кто-то поправил мне карму с 0 на -1.

В Twisted использовались реактор и коллбэки, да и сейчас используются. На asyncio он никогда не переходил.

Может статья писалась ChatGPT потому что странно, что даже информация в самой статье противоречит друг другу

В этом посте я подробно расскажу о важной фиче, которая появилась в Python 3.5 — асинхронности

Неа, в python 3.5 завезли asyncio, а не асинхронность.

Есть разница между общим понятием асинхронности и asyncio. В общем понимании асинхронность это возможность выполнять несколько задач независимо. Это означает что асинхронность можно реализовать к примеру несколькими потоками. Только вот в случае потоков GIL будет переключаться между потоками и проверять надо ли ему работать или нет. asyncio решает эту проблему. В данной статье речь все же больше идет об asyncio

Модуль asyncio с методами async functions, async with, контексты и корутины — выбор средств асинхронности в Python неплохой. Комбинируя их, можно создавать программы с высокой параллельностью

asyncio это не про параллельность. В asyncio несколько задач не работают одновременно, пока одна задача ждет, другая может работать

Теперь поговорим об использовании асинхронности с популярными фреймворками — Django, Flask и FastAPI. Ни один из них не имеет встроенной поддержки асинхронности, но ее можно добавить с помощью внешних библиотек.

У FastAPI есть из коробки

С помощью ORM вы можете выполнять запросы к базе данных асинхронно, вместо того чтобы ждать ответа перед выполнением следующей задачи

Не обязательно с помощью ORM, ORM - это об удобстве работы и представлении данных в виде более сложных объектов, чем tuple/list/dict.

Так вы можете увеличить производительность приложения и оптимизировать использование ресурсов. Пример асинхронного подключения к СУБД:

Дальше идет пример. Я вот чего не понимаю, почему говорим про ORM, а используем  сырые SQL-запросы?

Обработка запросов ускоряется

Не совсем так, скорее процессорное время тратится более оптимизировано. Если использовать синхронный запрос, он отработает скорее всего даже быстрее, если сразу начнет выполнятся. На практике запрос сначала попадает в очередь т.к. приложение способно одновременно обрабатывать к примеру только 4 запроса и начинает выполняться только тогда, когда приходит его очередь.
В случае использования async/awit тоже есть очередь, но в момент IO задач приложение может взять новый запрос, что невозможно в случае с синхронным выполнением.

Не все базы данных поддерживают асинхронный режим работы — например реляционные базы данных, такие как PostgreSQL и MySQL.

Эмм, чего? Весь ввод/вывод это IO задачи, как там работают бд нам на самом деле и не важно.

При использовании таких баз данных с асинхронным кодом вам могут потребоваться специальные библиотеки типа asyncpg.

Это всего лишь способ подключения, базу данных при этом asyncpg магическим образом не превращает в другую

С тем же успехом мы можем сказать что ОС работает синхронно, потому что для удаления файлов есть os.remove

Мне кажется автору стоит разобраться в БД/ORM/Асинхронности/Asyncio

зачем писали статью, но ведь вода одна, причем "местами" видно, что нет глубины понимания, согласен с выше "Мне кажется автору стоит разобраться в БД/ORM/Асинхронности/Asyncio".

Sign up to leave a comment.