上下文中使用异步IO
既然你已经接触了大量异步代码,不妨稍作停顿,回顾一下异步 I/O 在何种情况下是理想的选择,以及如何判断它是否适用,或者是否应该考虑其他并发模型。
什么时候使用异步IO
使用 `async def` 定义执行阻塞操作(例如标准文件 I/O 或同步网络请求)的函数,会阻塞整个事件循环,抵消异步 I/O 的优势,并可能降低程序的效率。仅在执行非阻塞操作时使用 `async def` 函数。(async def 定义的函数中执行阻塞操作,整个事件循环就会被卡住,其他协程就无法执行,异步的优势就全没了)
异步 I/O 与多进程之间的竞争并非真正的竞争。如果需要,可以同时使用这两种模型。在实践中,如果存在多个 CPU 密集型任务,多进程通常是合适的选择。
异步 I/O 与线程之间的竞争则更为直接。线程并非简单易用,即使在看似容易实现线程的场景中,也可能因竞态条件和内存使用等问题导致难以追踪的错误。
线程的扩展性通常不如异步 I/O 优雅,因为线程是一种有限的系统资源。在许多机器上,创建数千个线程可能会失败,或者会减慢代码的运行速度。相比之下,创建数千个异步 I/O 任务是完全可行的。
当存在多个 I/O 密集型任务时,异步 I/O 的优势尤为明显,这些任务如果不使用异步 I/O,将主要被阻塞等待时间所占据,例如:
网络 I/O,无论你的程序是作为服务器还是客户端运行。
无服务器架构,例如类似群聊的点对点、多用户网络。
读写操作,你希望采用类似于“发送即忘”的方式,而无需担心持有资源锁。
不使用异步 I/O 的最大原因是 await 只支持定义了特定方法的特定对象集合。例如,如果你想对某个数据库管理系统(DBMS)执行异步读取操作,那么你需要找到一个支持 async 和 await 语法的该 DBMS 的 Python 封装库。
支持异步IO的库
在 Python 中,你会找到许多高质量的第三方库和框架,它们支持或基于 `asyncio` 构建,包括用于 Web 服务器、数据库、网络、测试等的工具。以下是一些最值得关注的:
Web 框架:
FastAPI:用于构建 Web API 的现代异步 Web 框架。
Starlette:轻量级异步服务器网关接口(ASGI)框架,用于构建高性能异步 Web 应用。
Sanic:基于 asyncio 构建的高速异步 Web 框架。
Quart:与 Flask 具有相同 API 的异步 Web 微框架。
Tornado:高性能 Web 框架和异步网络库。
ASGI 服务器:
uvicorn:快速的 ASGI Web 服务器。
Hypercorn:支持多种协议和配置选项的 ASGI 服务器。
网络工具:
aiohttp:使用 asyncio 实现的 HTTP 客户端和服务器。
HTTPX:功能完备的异步和同步 HTTP 客户端。
websockets:使用 asyncio 构建 WebSocket 服务器和客户端的库。
aiosmtplib:用于发送电子邮件的异步 SMTP 客户端。
数据库工具:
Databases:与 SQLAlchemy 核心兼容的异步数据库访问层。
Tortoise ORM:轻量级异步对象关系映射器(ORM)。
Gino:基于 SQLAlchemy 核心为 PostgreSQL 构建的异步 ORM。
Motor:基于 asyncio 构建的异步 MongoDB 驱动程序。
实用工具库:
aiofiles:为异步和
await使用封装 Python 的文件 API。aiocache:支持 Redis 和 Memcached 的异步缓存库。
APScheduler:支持异步任务的调度器。
pytest-asyncio:为 pytest 添加异步函数测试支持。
这些库和框架帮助你编写高效的异步 Python 应用程序。无论是构建 Web 服务器、通过网络获取数据,还是访问数据库,像这样的 `asyncio` 工具都能让你以最小的开销同时处理多个任务。
总结
你已经对 Python 的 asyncio 库以及 async 和 await 语法有了扎实的理解,了解到异步编程如何在单个线程内高效地管理多个 I/O 密集型任务。
在学习过程中,你探讨了并发、并行、线程、多进程和异步 I/O 之间的区别。你还通过使用协程、事件循环、链式调用和基于队列的并发处理等实际示例进行了实践。此外,你还学习了高级的 asyncio 特性,包括异步上下文管理器、异步迭代器、推导式,以及如何利用第三方异步库。
掌握 asyncio 对于构建可扩展的网络服务器、Web API 或执行大量同时 I/O 密集型操作的应用程序至关重要。
