асинхронный код работает синхронно, кажется, нет строк, которые будут блокировать - PullRequest
0 голосов
/ 11 октября 2018

Работает в Windows 10, Python 3.6.3, работает в PyCharm IDE, этот код:

import asyncio
import json
import datetime
import time
from aiohttp import ClientSession


async def get_tags():
    url_tags = f"{BASE_URL}tags?access_token={token}"
    async with ClientSession() as session:
        async with session.get(url_tags) as response:
            return await response.read()


async def get_trips(vehicles):
    url_trips = f"{BASE_URL}fleet/trips?access_token={token}"
    for vehicle in vehicles:
        body_trips = {"groupId": groupid, "vehicleId": vehicle['id'], "startMs": int(start_ms), "endMs": int(end_ms)}
        async with ClientSession() as session:
            async with session.post(url_trips, json=body_trips) as response:
                yield response.read()


async def main():
    tags = await get_tags()
    tag_list = json.loads(tags.decode('utf8'))['tags']
    veh = tag_list[0]['vehicles'][0:5]
    return [await v async for v in get_trips(veh)]

t1 = time.time()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
t2 = time.time()
print(t2 - t1)

, кажется, работает полностью синхронно, время линейно увеличивается с увеличением размера цикла.Следуя примерам из книги, которую я прочитал, «Использование Asyncio в Python 3», код должен быть асинхронным;я что-то здесь упускаю?Подобный код в C # завершается за несколько секунд с примерно 2000 запросами, для выполнения 20 запросов требуется около 14 секунд (от 6 до 10).

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

переписал некоторый код:

async def get_trips(vehicle):
    url_trips = f"{BASE_URL}fleet/trips?access_token={token}"
    #for vehicle in vehicles:
    body_trips = {"groupId": groupid, "vehicleId": vehicle['id'], "startMs": int(start_ms), "endMs": int(end_ms)}
    async with ClientSession() as session:
        async with session.post(url_trips, json=body_trips) as response:
            res = await response.read()
            return res


t1 = time.time()
loop = asyncio.new_event_loop()
x = loop.run_until_complete(get_tags())
tag_list = json.loads(x.decode('utf8'))['tags']
veh = tag_list[0]['vehicles'][0:10]
tasks = []
for v in veh:
    tasks.append(loop.create_task(get_trips(v)))

loop.run_until_complete(asyncio.wait(tasks))
t2 = time.time()
print(t2 - t1)

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

Простой вопрос: как добавить результат возвратазадание в список или словарь?Более сложный вопрос, может ли кто-нибудь объяснить, почему мой код в первом примере выполняется синхронно, в то время как код во 2-й части выполняется асинхронно?

Редактировать 2:

заменяя:

loop.run_until_complete(asyncio.wait(tasks))

с:

x = loop.run_until_complete(asyncio.gather(*tasks))

Исправляет простую проблему;теперь просто любопытно, почему понимание асинхронного списка не работает асинхронно

1 Ответ

0 голосов
/ 12 октября 2018

теперь просто любопытно, почему понимание асинхронного списка не выполняется асинхронно

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

for vehicle in vehicles:
    trips = await fetch_trips(vehicle)
    # do something with trips

Чтобы сделать его параллельным, вы можете использовать wait или gather, как вы уже обнаружили, но это не обязательно.Как только вы создадите задачу, она будет выполняться параллельно.Например, это также должно работать:

# step 1: create the tasks and store (task, vehicle) pairs in a list
tasks = [(loop.create_task(get_trips(v)), v)
         for v in vehicles]

# step 2: await them one by one, while others are running:
for t, v in tasks:
    trips = await t
    # do something with trips for vehicle v
...