Как python обрабатывает асинхронные задачи

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

Сейчас я занялся Python/Django и дела идут хорошо, но мне очень интересно, как python обрабатывает любые асинхронные задачи, когда мне нужно сделать запрос к базе данных в django, я просто использую метод queryset, как

def func(id):
    do_somthing()
    user = User.objects.get(id=id)
    do_somthing_with_user()


func()
another_func()

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

Также, когда нам нужно сделать внешний запрос из python, мы используем метод библиотеки requests без ключевого слова await,

r = request.get("https://api.jsonplaceholder.com/users")
# will the below code executes when the response of the request arrive?
do_somthing()
.
.
.
do_another_thing()

блокирует ли python выполнение кода до получения ответа?

В JS асинхронная задача обрабатывается циклом событий, в то время как остальной код выполняется нормально

console.log("first")

fetch("https://api.jsonplaceholder.com/users").then(() => console.log("third"))

console.log("second")

логи в консоли будут

первый второй третий

Как Python обрабатывает подобные вещи под капотом?

В Python сетевые операции, такие как запрос к веб-серверу или чтение из сокета, могут блокировать выполнение кода до получения ответа. Это означает, что пока программа Python ожидает ответа, она не может выполнять никакие другие операции.

Например, если вы используете библиотеку urllib или requests для выполнения запроса к веб-серверу, выполнение кода будет заблокировано до получения ответа:

import requests
response = requests.get("http://example.com")
print(response.text)

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

Однако Python предоставляет способы избежать блокировки выполнения кода с помощью asyncio, модуля для параллельного программирования в Python. С помощью asyncio вы можете использовать ключевые слова async и await для определения асинхронных функций и использовать их для выполнения сетевых операций без блокирования выполнения кода.

Например, библиотека aiohttp предоставляет async версию библиотеки requests, которую вы можете использовать для выполнения запросов к веб-серверу без блокировки выполнения кода:

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        response = await fetch(session, "http://example.com")
        print(response)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Таким образом, выполнение кода не будет заблокировано, и вы сможете выполнять другие операции в ожидании ответа.

Команда await должна использоваться специально, когда вы запускаете асинхронную функцию, которая вызывает синхронные функции. Эта синхронная функция выполняется как coroutine и при отладке имеет имя объекта coroutine. Чтобы запустить coroutine, она должна быть запланирована в цикле событий. Использование await возвращает управление обратно циклу событий. Вместо того, чтобы ожидать корутину, вы также можете настроить задачу, чтобы запланировать выполнение корутины в определенное время, используя пакет asyncio. См. документацию

asyncio.create_task(coro, *, name=None, context=None)

Python не блокирует выполнение, а скорее гарантирует, что оно произойдет в нужное время, используя параллелизм процессора. Использование такого метода, как request.get(), не должно создавать никаких предстоящих ошибок, поскольку вы обращаетесь к значениям через Интернет, а не локально. Сетевые запросы перемещаются, по сути, со скоростью света, в то время как запросы к базе данных на сервере ограничены скоростью SSD или HDD. Это было больше проблемой на старых дисках, где вы буквально ждете, пока диск раскрутится, но идея правильного управления этими локальными ресурсами все еще остается проблемой, которую необходимо решить, чтобы асинхронное приложение работало. При выполнении вызовов к базе данных sql убедитесь, что используете декоратор @database_sync_to_async над обычными "синхронными" функциями, которые вызывает ваша асинхронная функция.

from channels.db import database_sync_to_async

Для получения дополнительной информации ознакомьтесь с документацией django по асинхронной поддержке.

Вернуться на верх