假设你要买三杯奶茶,每杯制作需要2分钟:
传统方式(同步):
def 买奶茶_同步():
for _ in range(3):
等待2分钟() # 干等着不动
拿奶茶()
# 总耗时:3×2=6分钟 ❌
聪明方式(异步):
async def 买奶茶_异步():
订单列表 = [下单(), 下单(), 下单()] # 同时下单
await asyncio.gather(*订单列表) # 边等边玩手机
# 总耗时:2分钟 ✅
想象一个外卖小哥同时处理多个订单:
这就是协程的工作方式!不需要多个小哥(线程),一个就能高效完成任务。
import asyncio
async def 打招呼(name): # 关键1:async定义协程
print(f"{name}开始做事")
await asyncio.sleep(1) # 关键2:遇到等待就挂起
print(f"{name}事情做完啦")
async def 主任务():
await asyncio.gather(
打招呼("小明"),
打招呼("小红")
)
asyncio.run(主任务()) # 关键3:启动事件循环
输出结果:
小明开始做事
小红开始做事
(等待1秒)
小明事情做完啦
小红事情做完啦
要素 | 说明 | 类比 |
---|---|---|
async def | 声明协程函数 | 给外卖订单贴上"加急"标签 |
await | 暂停并让出控制权 | 小哥暂时离开去送其他订单 |
事件循环 | 协调所有任务的调度员 | 外卖平台派单系统 |
错误:在普通函数中使用await
def 普通函数():
await asyncio.sleep(1) # 报错!
错误:忘记创建任务
async def 错误示例():
# 顺序执行,没有并发!
await 任务1()
await 任务2()
正确做法:
async def 正确示例():
task1 = asyncio.create_task(任务1())
task2 = asyncio.create_task(任务2())
await task1
await task2
import requests
def 下载图片(url):
print(f"开始下载 {url}")
data = requests.get(url).content
with open("图片.jpg", "wb") as f:
f.write(data)
print(f"下载完成 {url}")
def 主函数():
urls = ["url1", "url2", "url3"] # 假设3个图片地址
for url in urls:
下载图片(url)
# 总耗时:单张耗时 × 数量
import aiohttp
async def 异步下载(url):
async with aiohttp.ClientSession() as session:
print(f"开始下载 {url}")
async with session.get(url) as response:
data = await response.read()
with open(f"{url.split('/')[-1]}", "wb") as f:
f.write(data)
print(f"下载完成 {url}")
async def 主任务():
urls = ["url1", "url2", "url3"]
await asyncio.gather(*[异步下载(url) for url in urls])
asyncio.run(主任务())
图片数量 | 同步耗时 | 异步耗时 | 速度提升 |
---|---|---|---|
10 | 20s | 2s | 10倍 |
100 | 200s | 5s | 40倍 |
特性 | 协程 | 多线程 |
---|---|---|
资源占用 | 一个线程搞定所有 | 每个线程需要独立资源 |
切换方式 | 主动让出控制权 | 被系统强制切换 |
适用场景 | 适合大量IO操作 | 适合计算密集型任务 |
编程难度 | 需要理解异步语法 | 需要处理线程安全问题 |
使用asyncio.run()
作为入口
在协程内使用普通print语句
使用专业调试器:
import logging
logging.basicConfig(level=logging.DEBUG)
asyncio.gather
实现并发项目类型 | 实现功能 | 技能点 |
---|---|---|
天气查询器 | 同时查询多个城市天气 | 基础异步请求 |
网页监控 | 定时检查多个网站状态 | 异步定时任务 |
聊天机器人 | 同时处理多个用户消息 | 并发消息处理 |
此文由 Mix Space 同步更新至 xLog
原始链接为 https://blog.kanes.top/posts/default/asyncio