Сделайте асинхронный 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())