Python asyncio 模块详解
编程小白也能懂的异步编程指南 – 用通俗语言解释核心概念
什么是 asyncio?
asyncio 是 Python 中用于编写并发代码的库,使用 async/await 语法。它主要用于处理 I/O 密集型任务(如网络请求、文件读写等),让程序在等待这些操作完成时可以做其他事情。
🍽️ 餐厅比喻
想象一家只有一个服务员的餐厅:
- 同步方式:服务员点完一桌菜后,站在厨房门口等厨师做完,再端给顾客,然后再服务下一桌。效率很低!
- 异步方式:服务员点完A桌菜后,告诉厨师去做,然后马上去服务B桌。厨师做好后会通知服务员,服务员再去端菜。
asyncio 就是这个高效的服务员,让程序在等待时也能做其他事!
核心概念
🔄
1. 事件循环 (Event Loop)
asyncio 的核心引擎,负责协调和管理所有任务。就像一个交通警察,指挥车辆(任务)何时运行、何时暂停。
⏸️▶️
2. 协程 (Coroutine)
使用 async def
定义的函数,可以在执行中暂停和恢复。协程是异步编程的基本单位。
🔧
3. Task 对象
对协程的进一步封装,把协程加入事件循环中调度执行。就像给协程分配一个具体的”工作任务”。
⏳
4. Future 对象
表示一个”未来完成的操作”,是Task的基类。用于在低级代码中表示异步操作的结果。
基本语法
async/await 关键字
async def
:用于定义异步函数(协程)await
:用于等待一个协程完成(交出控制权)
import asyncio
# 定义一个协程
async def main():
print(‘开始执行’)
await asyncio.sleep(1) # 等待1秒(模拟I/O操作)
print(‘1秒后’)
# 运行协程
asyncio.run(main())
# 定义一个协程
async def main():
print(‘开始执行’)
await asyncio.sleep(1) # 等待1秒(模拟I/O操作)
print(‘1秒后’)
# 运行协程
asyncio.run(main())
运行多个协程
使用 asyncio.gather()
同时运行多个协程:
import asyncio
async def task1():
await asyncio.sleep(2)
print(‘任务1完成’)
async def task2():
await asyncio.sleep(1)
print(‘任务2完成’)
async def main():
await asyncio.gather(task1(), task2())
print(‘所有任务完成!’)
asyncio.run(main())
async def task1():
await asyncio.sleep(2)
print(‘任务1完成’)
async def task2():
await asyncio.sleep(1)
print(‘任务2完成’)
async def main():
await asyncio.gather(task1(), task2())
print(‘所有任务完成!’)
asyncio.run(main())
📌 重要提示:asyncio.sleep()
是异步等待,而 time.sleep()
是阻塞等待。在异步函数中一定要使用 asyncio.sleep()
!
创建任务
使用 asyncio.create_task()
将协程包装成任务,以便并发执行:
async def main():
task = asyncio.create_task(some_coroutine())
# 可以在这里执行其他操作
await task # 等待任务完成
task = asyncio.create_task(some_coroutine())
# 可以在这里执行其他操作
await task # 等待任务完成
同步 vs 异步 vs 多线程
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
同步 | 简单直观,易于理解 | 效率低,资源浪费 | 简单脚本,CPU密集型任务 |
多线程 | 可以并行执行,利用多核CPU | 线程安全问题,调试困难 | CPU密集型任务 |
异步(asyncio) | 高效利用资源,适合I/O操作 | 学习曲线较陡,代码结构复杂 | I/O密集型任务(网络、文件等) |
实际应用场景
- 网络应用(Web服务器、API客户端)
- 网络爬虫
- 数据库操作
- 实时数据处理
- 聊天服务器
学习建议
- 先掌握
async/await
基本语法 - 理解事件循环概念
- 从简单示例开始,逐步构建复杂应用
- 多练习 I/O 密集型任务的异步实现
- 注意异步函数中不要使用阻塞操作