异步代码同步运行,似乎没有任何行会阻塞

时间:2018-10-11 17:11:17

标签: python-3.6 python-asyncio aiohttp

在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)

似乎完全同步运行,时间随着循环大小的增加而线性增加。遵循我读过的一本书中的示例“在Python 3中使用Asyncio”,代码应该是异步的。我在这里想念什么吗? C#中类似的代码可在几秒钟内完成大约2,000个请求,在这里大约需要14s来运行20个请求(6s来运行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:

替换:

loop.run_until_complete(asyncio.wait(tasks))

具有:

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

解决了简单的问题;现在只是好奇为什么异步列表推导不能异步运行

1 个答案:

答案 0 :(得分:1)

  

现在很好奇为什么异步列表理解不能异步运行

因为您的理解会在异步生成器上进行迭代,该生成器会生成单个任务,然后立即等待它,从而杀死了并行性。大致相当于:

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

要使其平行,可以使用已经发现的waitgather,但这不是强制性的。创建任务后,它将并行运行。例如,这也应该工作:

# 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