asyncio 简介#
asyncio 是 Python 标准库的一部分,它提供了一个带有 IO(输入/输出)操作的事件循环。它的存在是为了在 Python 中允许并发编程,事件循环在之前的任务等待 IO 时切换到另一个任务。这种并发性允许更高的 CPU 利用率,从而带来更高的吞吐量性能。
理解这一点最简单的方法是考虑一些具体的东西,比如一个演示模拟。在下面,我们使用模拟的 IO 延迟获取一个 url,
import asyncio
async def simulated_fetch(url, delay):
await asyncio.sleep(delay)
print(f"Fetched {url} after {delay}")
return f"<html>{url}"
def main():
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(
simulated_fetch('http://google.com', 2),
simulated_fetch('http://bbc.co.uk', 1),
))
print(results)
你应该看到以下输出,
>>> Fetched http://bbc.co.uk after 1
>>> Fetched http://google.com after 2
>>> ['<html>http://google.com', '<html>http://bbc.co.uk']
这表明尽管首先调用了 google.com
获取,但 bbc.co.uk
实际上先完成,即代码并发运行。此外,代码在不到 2 秒的时间内运行,而不是像同步代码那样超过 3 秒。
与 Web 服务器的相关性#
Web 服务器本质上进行 IO,因为它们接收和响应来自网络的请求。这意味着 asyncio 非常适合,即使框架内的代码本身不执行 IO。然而在实践中 IO 是存在的,例如从文件加载模板,或者联系数据库或其他服务器。
常见陷阱#
很容易等待错误的东西,例如,
await awaitable.attribute
不会像你期望的那样等待 awaitable
对象,而是尝试解析属性并等待它。这在以下情况中很常见,
await request.form.get('key')
这会导致错误,因为协程包装器没有 get 属性。为了解决这个问题,只需使用括号来指示首先需要等待的内容,
(await awaitable).attribute
(await request.form).get('key')