马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
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/Headersaiohttp(异步)异步订单体系跟踪每个异步使命的上下文,确保哀求与相应逐一对应完备代码
- 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
|