Python 协程和异步编程入门教程
1. 同步与异步、并发与并行
在传统的同步(synchronous)模型中,函数调用会阻塞当前线程,直到操作完成才继续执行;而在异步(asynchronous)模型中,函数可以在等待 I/O 或其他耗时操作时,主动“让出”执行权,让事件循环去调度其他任务,从而提升程序的并发性。
并发(concurrency)指在逻辑上同时处理多个任务,强调任务的交替执行;
并行(parallelism)则是真正利用多核 CPU 同时执行多个任务。
异步编程属于并发范畴,适合 I/O 密集型场景,如网络请求、文件读写等。
2. 协程(Coroutine)简介
Python 中的协程是可暂停并恢复的函数,它本质上是生成器(generator)的扩展。调用带有 async def
定义的函数会返回一个协程对象,该对象只有在被 await
或调度到事件循环中时才会执行。
async def foo():
print("开始")
await asyncio.sleep(1)
print("结束")
上述 foo()
并不会立即执行,只有在 await foo()
或 asyncio.create_task(foo())
后才会调度运行。
3. async
/ await
语法
使用
async def
声明协程函数;在协程内部,用
await
调用另一个协程或可等待对象(Awaitable),等待其完成并获取结果。
import asyncio
async def hello():
print("Hello")
await asyncio.sleep(1)
print("World")
asyncio.run(hello())
asyncio.run()
会启动一个新的事件循环并运行传入的顶层协程,直到其结束。
4. asyncio
库与事件循环
asyncio
是 Python 标准库提供的异步 I/O 框架,核心组件包括:
事件循环(Event Loop):负责调度协程和其他 I/O 事件;
任务(Task):对协程的封装,可并发运行多个任务;
Future:表示尚未完成的操作结果,通常由底层 I/O 或任务产生。
loop = asyncio.get_event_loop()
task = loop.create_task(hello())
loop.run_until_complete(task)
现代用法推荐直接使用 asyncio.run()
,无需手动管理循环。
5. 任务(Task)与调度
asyncio.create_task(coro)
:将协程封装为任务并立即调度;await task
:等待任务完成并返回结果;可以通过
asyncio.gather()
同时并发等待多个任务;任务支持取消(
task.cancel()
)和超时(asyncio.wait_for()
)等高级操作。
async def main():
tasks = [asyncio.create_task(foo(i)) for i in range(5)]
results = await asyncio.gather(*tasks, return_exceptions=True)
print(results)
6. 实战示例:并发网络请求
下面示例演示如何使用 aiohttp
并发抓取多个网页标题:
import asyncio
import aiohttp
from bs4 import BeautifulSoup
async def fetch(session, url):
async with session.get(url) as resp:
text = await resp.text()
title = BeautifulSoup(text, 'html.parser').title.string
return url, title
async def main(urls):
async with aiohttp.ClientSession() as session:
tasks = [asyncio.create_task(fetch(session, u)) for u in urls]
for coro in asyncio.as_completed(tasks):
url, title = await coro
print(f"{url} -> {title}")
if __name__ == "__main__":
urls = ["https://www.python.org", "https://realpython.com", "https://docs.python.org"]
asyncio.run(main(urls))
上述代码中,aiohttp.ClientSession
提供异步 HTTP 客户端,asyncio.as_completed
可逐个处理完成的任务,提高响应速度。
7. 异常处理与调试
在协程内部使用常规的
try/except
捕获异常;对于
asyncio.gather()
,可通过return_exceptions=True
收集各任务异常;使用
asyncio.TaskGroup
(Python 3.11+)组织任务,并自动管理取消和异常传播;可结合
asyncio.Logger
和loop.set_exception_handler()
进行全局异常监控。
async def safe_fetch(session, url):
try:
return await fetch(session, url)
except Exception as e:
return url, f"Error: {e}"
8. 最佳实践与注意事项
避免在协程中执行 CPU 密集型任务,否则会阻塞事件循环,可考虑使用线程池或进程池;
合理设置并发量,如对网络请求使用信号量(
asyncio.Semaphore
)限制并发数;及时关闭资源,如
ClientSession
、文件句柄等;分层封装,将 I/O 操作与业务逻辑分离,便于测试和维护;
熟悉调试工具,如
asyncio.run(..., debug=True)
、PYTHONASYNCIODEBUG
环境变量等。
9. 小结
本文介绍了 Python 协程的基本概念、async
/await
语法、asyncio
核心组件以及实战示例,并给出异常处理与最佳实践建议。掌握这些内容后,你就能在 I/O 密集型场景中,用异步编程显著提升程序的并发性能。若要深入学习,可参考官方文档和社区优质教程,逐步构建更复杂的异步系统。