Сделайте асинхронный HTTP-запрос и продолжите выполнение цикла

У меня есть простая функция Python со следующим псевдокодом:

while True:

    # 1 - CPU Intensive calculations(Synchronous)
    
    
    # 2 - Take the result from the synchronous calculation and POST it to a server

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

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

редактировать


Перед тем, как разместить здесь вопрос, я попробовал этот подход:

async def example_function():
    # Synchronous CPU Intensive calculations

    client = httpx.AsyncClient()
    # Make a call to the REST API
    await client.post()


async def main():
    while True:
        await asyncio.gather(example_function(), example_function())


if __name__ == "__main__":
    asyncio.run(main())

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

1 ответ

Решение

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

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

def calculations(arg1):
    # ... synchronous code here

async def post_result(result, session):
    async with session.post('http://httpbin.org/post',
                            data={'key1': result}) as resp:
        resp.raise_for_status()

async def main():
    loop = asyncio.get_event_loop()

    with aiohttp.ClientSession() as session:
        while True:
            # run the sync code off-thread so the event loop
            # continues running, but wait for it to finish
            result = await loop.run_in_executor(None, calculations, arg1)

            # don't await the task, let it run in the background
            asyncio.create_task(post_result(result, session))

if __name__ == '__main__':
    asyncio.run(main())
Другие вопросы по тегам