Python异步编程教程_asyncio协程与事件循环实践

Python异步编程核心是asyncio,通过协程和事件循环实现单线程并发,适用于I/O密集型任务;协程需用async/await定义与调用,必须由事件循环调度执行;事件循环是调度中心,asyncio.run()自动管理;并发用gather或create_task;CPU密集型任务需配合executor,须使用异步兼容库,避免同步阻塞操作。

Python异步编程的核心是 asyncio,它通过协程(coroutine)和事件循环(event loop)实现单线程并发,特别适合 I/O 密集型任务,比如网络请求、文件读写、数据库查询等。关键不是“多线程”,而是“让出控制权”,避免等待时的空转。

协程:用 async/await 定义和调用

协程函数用 async def 声明,调用后返回一个协程对象,**不会立即执行**。必须交给事件循环运行,或用 await 在另一个协程中等待。

  • 直接调用 my_coro() 只得到一个协程对象,不运行;
  • await my_coro() 只能在 async 函数内部 使用;
  • 顶层入口要用 asyncio.run(my_coro()) 启动事件循环并运行协程。

事件循环:asyncio 的“调度中心”

事件循环是 asyncio 的核心运行时,负责注册、调度和执行协程、回调、定时任务等。日常开发中大多不需要手动创建,asyncio.run() 会自动创建、运行并关闭它。

  • 想手动控制循环?可用 loop = asyncio.get_event_loop()(注意 Python 3.11+ 推荐用 asyncio.get_running_loop());
  • 循环一旦关闭,就不能再复用;多次调用 run() 会各自新建循环;
  • 阻塞操作(如 time.sleep())会卡住整个循环,务必改用 await asyncio.sleep()

并发执行多个协程:asyncio.gather 与 create_task

要同时发起多个异步任务,不能简单用 await 串行等待,而应并发调度。

  • await asyncio.gather(coro1(), coro2(), coro3()):批量等待多个协程,全部完成才返回结果列表;
  • task = asyncio.create_task(coro()):立即调度协程为任务(Task 对象),适合需要提前启动、后续再 await 的场景;
  • 多个 task 可以分别 await,也可用 asyncio.wait() 等待任意完成或超时。

常见陷阱与注意事项

异步不是万能的,用错反而降低性能甚至引发错误。

  • CPU 密集型任务(如大数计算、图像处理)不适合纯 asyncio,应配合 loop.run_in_executor() 丢给线程池或进程池;
  • 第三方库必须明确支持 asyncio(如 aiohttp 替代 requestsaiomysql 替代 PyMySQL);
  • 不要在协程里混用同步 I/O(如 open()、requests.get()),否则会阻塞事件循环;
  • 调试异步代码时,留意 “RuntimeWarning: coroutine ‘xxx’ was never awaited” —— 这说明你忘了 await 或 run。
异步编程的本质是协作式并发,靠显式 await 让出控制权。理解协程生命周期、事件循环职责和 I/O 非阻塞原理,比记住语法更重要。