当所有协同程序都在等待时,asyncio会侦听事件以再次唤醒它们 . 一个常见的例子是 asyncio.sleep()
,它记录一个定时事件 . 实际上,事件通常是准备接收或发送新数据的IO套接字 .
为了更好地理解这种行为,我设置了一个简单的测试:它向localhost发送一个http请求并等待响应 . 在localhost上,我已经设置了一个烧瓶服务器,在响应之前等待1秒钟 . 发送请求后,客户端休眠1秒钟,然后等待响应 . 我希望这会在一秒钟内返回,因为我的程序和服务器应该并行睡眠 . 但它需要2秒钟:
import aiohttp
import asyncio
from time import perf_counter
async def main():
async with aiohttp.ClientSession() as session:
# this http request will take 1 second to respond
async with session.get("http://127.0.0.1:5000/") as response:
# yield control for 1 second
await asyncio.sleep(1)
# wait for the http request to return
text = await response.text()
return text
loop = asyncio.get_event_loop()
start = perf_counter()
results = loop.run_until_complete(main())
stop = perf_counter()
print(f"took {stop-start} seconds") # 2.01909
What is asyncio doing here, why can't I overlap waiting times ?
我对HTTP请求的具体场景不感兴趣,aiohttp仅用于构造示例 . 这可能有点危险:这可能与aiohttp有关,而不是asyncio .
实际上,我预计会出现这种情况(因此关于asyncio和aiohttp的问题 Headers ) . 我的第一个直觉是在调用_1027669之前可能没有发送请求所以我重新排序了一下:
# start coroutine
text = response.text()
# yield control for 1 second
await asyncio.sleep(1)
# wait for the http request to return
text = await text
但这还需要两秒钟 .
好的,现在要确保请求在进入睡眠状态之前被发送,我将 print("incoming")
添加到服务器上的路由,然后再进入休眠状态 . 我还在客户端将睡眠时间长度更改为10秒 . 服务器在客户端运行后立即打印传入 . 客户端总共需要11秒 .
@app.route('/')
def index():
print("incoming")
time.sleep(1)
return 'done'
由于HTTP请求是立即生成的,服务器肯定在客户端从_1027673唤醒之前发出了答案 . 在我看来,提供HTTP请求的套接字应该在客户端唤醒后立即就绪 . 但是,总运行时间总是增加客户端和服务器等待时间 .
我是不是在某种程度上滥用asyncio,或者这毕竟与aiohttp有关?
2 回答
问题是服务器中发生的一秒钟在
async with session.get("http://127.0.0.1:5000/") as response:
中执行 .http请求在您获得此
response
对象之前完成 .您可以通过以下方式测试:
顺便说一下,只要你有另一个正在运行的协程,你肯定可以重叠这个等待时间 .
您的测试代码有三个等待(两个显式和一个隐藏在
async with
中),因此您不会得到任何并行等待 . 测试您描述的场景的代码类似于:运行此协程应该花费预期的时间 .