Python asyncio 异步编程完全指南
目录
- 异步编程核心思想
- 同步 vs 异步 直观对比
- 网络请求对比案例
- 简化Demo(Hello→World)
- 协程基础
- 协程概念与特点
- async/await 关键字详解
- 协程函数示例(单个/多个任务)
- 事件循环(Event Loop)
- 核心原理
- 手动配置事件循环示例
- 异步IO操作
- 异步文件读写
- 关键注意事项
一、异步编程核心思想
异步编程是单线程中并发处理多任务的编程方式,核心优势是提升IO密集型任务(网络通信、数据库访问、文件读写)的并发性和响应性。
其核心思想是:避免阻塞——当一个任务等待IO操作(如等待网络响应、等待文件读取)时,主动让出CPU执行权,让其他任务继续执行,从而充分利用CPU时间片,避免闲置浪费。
生动类比(餐厅服务员模型)
| 模式 | 执行流程 | 核心特点 |
|---|---|---|
| 同步模式 | 接待顾客A → 记录订单 → 原地等菜 → 给A上菜 → 再接待顾客B | 等待IO时CPU闲置,效率低 |
| 异步模式 | 接待顾客A → 记录订单 → 转接待顾客B → 接待顾客C → 后厨喊菜 → 暂停C,给A上菜 → 继续接待C | 等待IO时切换任务,CPU不闲置 |
越来越多现代语言(如JavaScript、Python)支持异步编程,asyncio是Python 3.4+内置的标准库,基于事件循环和协程实现异步调度。
二、同步 vs 异步 直观对比
1. 网络请求对比案例(模拟5次延迟请求)
通过 requests(同步)和 aiohttp(异步)对比耗时,直观感受异步优势。
对比结果

关键差异:同步需依次等待每个请求完成(总耗时≈2×5=10秒),异步同时发起所有请求(总耗时≈2秒)
核心区别:Session的作用
| 库 | Session 定位 | 核心价值 |
|---|---|---|
| requests(同步) | 同步优化工具 | 复用TCP连接(减少三次握手)、共享Cookie/Headers |
| aiohttp(异步) | 异步订单系统 | 跟踪每个异步任务的上下文,确保请求与响应一一对应 |
完整代码
import time
import requests
import asyncio
import aiohttp
# ---------------------- 同步版本 ----------------------
def sync_fetch(url):
"""同步请求单个URL(阻塞式)"""
response = requests.get(url)
return f"同步:请求 {url} 完成,状态码 {response.status_code}"
def sync_main():
"""模拟5个同步请求"""
urls = ["https://httpbin.org/delay/2" for _ in range(5)]
start_time = time.perf_counter()
results = [sync_fetch(url) for url in urls]
end_time = time.perf_counter()
for res in results:
print(res)
print(f"\n【同步总耗时】:{end_time - start_time:.2f} 秒\n")
# ---------------------- 异步版本 ----------------------
async def async_fetch(session, url):
"""异步请求单个URL(非阻塞,让出CPU)"""
async with session.get(url) as response:
return f"异步:请求 {url} 完成,状态码 {response.status}"
async def async_main():
"""并发执行5个异步请求"""
urls = ["https://httpbin.org/delay/2" for _ in range(5)]
start_time = time.perf_counter()
async with aiohttp.ClientSession() as session:
# 创建任务列表,并发执行
tasks = [async_fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks)
end_time = time.perf_counter()
for res in results:
print(res)
print(f"\n【异步总耗时】:{end_time - start_time:.2f} 秒")
# 运行对比
if __name__ == "__main__":
sync_main()
asyncio.run(async_main())
2. 简化Demo(直观理解任务切换)
通过「Hello→等待1秒→World」的双任务对比,快速理解异步并发的执行逻辑。
运行效果

完整代码
import asyncio
import time
# ---------------------- 异步版本 ----------------------
async def async_hello():
print("异步:Hello")
await asyncio.sleep(1) # 模拟IO等待,主动让出CPU
print("异步:World")
async def async_main():
start_time = time.perf_counter()
# 并发执行两个任务
await asyncio.gather(
async_hello(),
async_hello()
)
end_time = time.perf_counter()
print(f"【异步总耗时】:{end_time - start_time:.2f} 秒\n")
# ---------------------- 同步版本 ----------------------
def sync_hello():
print("同步:Hello")
time.sleep(1) # 阻塞CPU,无法切换任务
print("同步:World")
def sync_main():
start_time = time.perf_counter()
sync_hello()
sync_hello() # 串行执行,必须等前一个完成
end_time = time.perf_counter()
print(f"【同步总耗时】:{end_time - start_time:.2f} 秒\n")
# 运行对比
if __name__ == "__main__":
print("===== 同步执行 =====")
sync_main()
print("===== 异步执行 =====")
asyncio.run(async_main())
三、协程基础
1. 协程的概念和特点
协程(Coroutines)是asyncio的核心,本质是可暂停、可恢复的函数,具备以下特点:
- 单线程执行,通过「暂停/恢复」实现任务切换(无线程切换开销)
- 必须在事件循环中运行(无法直接调用协程函数)
- 暂停时会让出CPU,不阻塞其他任务执行
- 依赖
async/await关键字实现语法支持
2. async/await 关键字详解
| 关键字 | 作用 | 核心规则 | 使用场景 |
|---|---|---|---|
| async | 定义协程函数/异步上下文管理器 | 只能修饰 def(协程)或 with/for(异步上下文) | 1. 定义异步函数:async def func(): ... 2. 异步上下文: async with ... |
| await | 暂停当前协程,等待可等待对象完成 | 只能在 async def 定义的协程内部使用 | 1. 等待另一个协程:await coro() 2. 等待异步IO: await asyncio.sleep() 3. 等待任务集合: await asyncio.gather() |
注:「可等待对象」包括:协程(coroutine)、任务(Task)、未来对象(Future)
3. 协程函数示例
(1)最简化的协程(单个任务)
import asyncio
async def my_coroutine():
print("协程开始执行")
await asyncio.sleep(1) # 模拟1秒IO等待
return "协程执行结果"
# 启动协程(asyncio.run() 会自动创建事件循环)
result = asyncio.run(my_coroutine())
print("协程返回结果:", result)
(2)多任务聚合(统一调度多个协程)
实际开发中需调度多个协程,用 asyncio.gather() 聚合任务,统一处理结果:
import asyncio
async def task(name):
print(f"【{name}】启动")
await asyncio.sleep(1) # 模拟IO等待
return f"【{name}】完成"
async def main():
print("===== 主协程启动 =====")
# 创建3个并发任务
tasks = [task("任务1"), task("任务2"), task("任务3")]
# 等待所有任务完成,收集结果
results = await asyncio.gather(*tasks)
print("\n===== 所有任务执行完成 =====")
for res in results:
print(res)
if __name__ == "__main__":
asyncio.run(main())
四、事件循环(Event Loop)
1. 核心原理
事件循环是asyncio的「调度中心」,本质是单线程的无限循环,负责:
- 管理所有协程任务的生命周期(启动、暂停、恢复、结束)
- 监听IO事件(如网络响应、文件读取完成)
- 当任务暂停时,切换到其他就绪任务
- 当IO事件完成时,唤醒等待该事件的任务
生动类比
事件循环 = 餐厅「执行总监」:
- 分配任务给服务员(协程)
- 监督任务进度(监听IO事件)
- 当服务员等待(如等菜)时,安排其去服务其他顾客(切换任务)
- 当菜做好(IO完成)时,通知服务员上菜(唤醒任务)
2. 手动配置事件循环(进阶)
Python 3.7+ 推荐用 asyncio.run() 自动管理事件循环,但也可手动配置(适合复杂场景):
import asyncio
import time
async def my_coroutine():
print("协程开始执行")
await asyncio.sleep(1)
return "协程执行结果"
async def gather_tasks():
"""聚合多个协程任务"""
return await asyncio.gather(
my_coroutine(),
my_coroutine(),
my_coroutine()
)
if __name__ == "__main__":
start_time = time.perf_counter()
# 手动创建并绑定事件循环
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
# 运行直到任务完成
results = loop.run_until_complete(gather_tasks())
finally:
# 关闭事件循环(释放资源)
loop.close()
end_time = time.perf_counter()
print("任务结果:", results)
print(f"总耗时:{end_time - start_time:.2f} 秒")
运行结果

五、异步IO操作
异步编程的核心价值在于优化IO密集型任务,asyncio生态提供了丰富的异步IO工具(如 aiohttp 用于网络请求,aiofiles 用于文件读写)。
1. 异步文件读写(aiofiles)
传统 open() 是阻塞式的,aiofiles 提供异步文件操作API,避免IO等待时阻塞事件循环:
import asyncio
import aiofiles
async def read_file(file_path):
# 异步打开文件(async with 是异步上下文管理器)
async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
data = await f.read() # 异步读取,不阻塞事件循环
print("文件内容:")
print(data)
# 启动异步文件读取
asyncio.run(read_file('flag.txt'))
运行结果

2. 关键注意事项
- 异步IO必须使用「异步兼容库」:如
aiohttp(替代requests)、aiofiles(替代open()),不能在协程中直接使用阻塞式IO库(会导致整个事件循环阻塞) - 异步适合IO密集型任务:CPU密集型任务不适合用asyncio(单线程无法利用多核,反而不如多线程)
- 异步上下文管理器:异步操作资源时(如文件、网络连接),需用
async with自动管理资源(类似同步的with,但支持异步)
总结
- 核心价值:asyncio通过「协程+事件循环」实现单线程并发,解决IO密集型任务的阻塞问题,提升程序响应速度
- 关键语法:
async def定义协程,await暂停等待,asyncio.gather()聚合任务 - 适用场景:网络请求、数据库操作、文件读写等IO密集型场景
- 避坑点:不混用阻塞式IO库,CPU密集型任务优先用多进程(
multiprocessing)或concurrent.futures
该文章围绕 Python asyncio 异步编程展开全面讲解,先以生动的餐厅服务员类比阐释异步编程 “避免阻塞、提升 CPU 利用率” 的核心思想,对比同步与异步模式的执行差异。随后通过网络请求和简化的 Hello - World 两个实战案例,直观展现了 requests 同步库与 aiohttp 异步库的耗时差距,点明两者 Session 的不同定位。文章还详解了 async/await 关键字的用法、协程的定义与多任务聚合方式,深入剖析事件循环的调度原理并给出手动配置示例,最后介绍了基于 aiofiles 的异步 IO 操作,整体内容从基础概念到实战代码,逻辑清晰,兼顾原理讲解与落地应.
浙公网安备 33010602011771号