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 框架,核心组件包括:

  1. 事件循环(Event Loop):负责调度协程和其他 I/O 事件;

  2. 任务(Task):对协程的封装,可并发运行多个任务;

  3. 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.Loggerloop.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. 最佳实践与注意事项

  1. 避免在协程中执行 CPU 密集型任务,否则会阻塞事件循环,可考虑使用线程池或进程池;

  2. 合理设置并发量,如对网络请求使用信号量(asyncio.Semaphore)限制并发数;

  3. 及时关闭资源,如 ClientSession、文件句柄等;

  4. 分层封装,将 I/O 操作与业务逻辑分离,便于测试和维护;

  5. 熟悉调试工具,如 asyncio.run(..., debug=True)PYTHONASYNCIODEBUG 环境变量等。

9. 小结

本文介绍了 Python 协程的基本概念、async/await 语法、asyncio 核心组件以及实战示例,并给出异常处理与最佳实践建议。掌握这些内容后,你就能在 I/O 密集型场景中,用异步编程显著提升程序的并发性能。若要深入学习,可参考官方文档和社区优质教程,逐步构建更复杂的异步系统。