Skip to content

Python 异步编程

本文讲解基于 asyncio 的协程模型:事件循环、async/await、任务并发与常见陷阱。

作者:yanshaodong

为什么需要异步

同步代码中遇到 I/O(网络、磁盘)时线程会阻塞等待。异步通过协程在等待期间让出控制权,从而以单线程并发处理大量 I/O 任务,提升吞吐。

协程基础

python
import asyncio

async def fetch_data():
    await asyncio.sleep(1)    # 模拟 I/O,让出控制权
    return "data"

async def main():
    data = await fetch_data()
    print(data)

asyncio.run(main())
  • async def 定义协程函数,调用返回协程对象而非立即执行。
  • await 挂起当前协程,直到可等待对象完成。
  • asyncio.run() 创建事件循环并运行顶层协程。

并发执行任务

python
async def task(name, delay):
    await asyncio.sleep(delay)
    return f"{name} 完成"

async def main():
    # 并发:同时启动,总耗时取最长
    results = await asyncio.gather(
        task("A", 1),
        task("B", 2),
        task("C", 3),
    )
    print(results)   # ['A 完成', 'B 完成', 'C 完成']

gather 并发运行多个协程,约 3 秒完成(而非 1+2+3)。

Task 与 create_task

python
async def main():
    t1 = asyncio.create_task(task("A", 1))
    t2 = asyncio.create_task(task("B", 2))
    await t1
    await t2

create_task 立即调度协程为任务,适合在 await 之前就启动。

超时与取消

python
async def main():
    try:
        await asyncio.wait_for(task("X", 5), timeout=2)
    except asyncio.TimeoutError:
        print("超时")

    t = asyncio.create_task(task("Y", 10))
    t.cancel()
    try:
        await t
    except asyncio.CancelledError:
        print("已取消")

同步原语

python
lock = asyncio.Lock()

async def safe_inc():
    async with lock:
        # 临界区,同一时刻仅一个协程进入
        ...

还提供 asyncio.Eventasyncio.Queueasyncio.Semaphore 等。

异步上下文管理器与迭代器

python
class Conn:
    async def __aenter__(self):
        print("连接")
        return self
    async def __aexit__(self, *exc):
        print("关闭")

async def main():
    async with Conn():
        await asyncio.sleep(0.1)

与同步代码互操作

  • 在同步函数中调用异步:asyncio.run(coro)loop.run_until_complete
  • 在异步中运行阻塞调用:用 asyncio.to_thread(blocking_func) 避免阻塞事件循环。
  • 不要在协程里用同步 time.sleep(),应使用 await asyncio.sleep()

常见陷阱

  1. 忘记 await:协程对象不执行,仅被创建。
  2. 混用阻塞 I/O:在事件循环中调用同步阻塞函数会卡住所有协程。
  3. 在同步函数里直接调用协程:需通过 asyncio.run 或事件循环驱动。

小结

  • 异步适合 I/O 密集型,不适合 CPU 密集型(用多进程)。
  • async/await 是语法糖,底层是事件循环调度协程。
  • gather / create_task 实现并发;wait_for 控制超时。
  • 阻塞调用务必 to_thread 或异步库(如 aiohttpasyncpg)。