asyncio.gather против понимания списка

#python

#python

Вопрос:

В следующем коде get_all_details_1 одинаковы и get_all_details_2 ведут себя одинаково?

 async def get_details(category, limit):
    async with limit:
        # ... 

limit = asyncio.Semaphore(4)
a_list = .... # a big list

async def get_all_details_1():
    b_list = await asyncio.gather(*[get_details(x, limit) for x in a_list])
    # ....


async def get_all_details_2():
    b_list = [await get_details(x, limit) for x in a_list]
    # ....
 

Комментарии:

1. не совсем, но результат тот же… сбор, вероятно, немного быстрее

Ответ №1:

Абсолютно нет! Пример:

 import asyncio
import time

async def slowtask():
    await asyncio.sleep(1)

async def gather():
    await asyncio.gather(*[slowtask() for _ in range(10)])

async def listcomp():
    [await slowtask() for _ in range(10)]

start = time.time()
asyncio.run(gather())
print("gather", time.time() - start)

start = time.time()
asyncio.run(listcomp())
print("listcomp", time.time() - start)
 

дает нам:

 gather 1.0030405521392822
listcomp 10.015443325042725
 

asyncio.gather правильно позволяет нескольким асинхронным задачам выполняться асинхронно, в то время как понимание списка await выполняется одно за другим, что приводит к эффективному последовательному коду.

Комментарии:

1. Будет ли проблемой asyncio.gather(.....) принять очень длинный список аргументов?

2. @ca9163d9 Я не понимаю, почему это так.

3. Только до тех пор, пока производительность не начнет снижаться или выходить на плато при добавлении дополнительных сопрограмм в ваш сбор. Aiohttp запускает только 100 соединений одновременно, поэтому 10 000 запросов GET могут быть лучше распределены по многим асинхронным потокам или процессам.