作者: 你最贴心的技术小助手 😎
难度: 从零基础到精通
阅读时间: 建议泡杯咖啡 ☕,慢慢品味(约30分钟)
适合人群: 想让代码跑得飞快的你!
📚 目录
- 开场白:为什么要学 Asyncio?
- 第一章:什么是异步编程?
- 第二章:Asyncio 核心概念大揭秘
- 第三章:实战演练 - 从入门到精通
- 第四章:常见坑点与最佳实践
- 第五章:性能对比与实战应用
- 总结:你已经是异步编程高手了!
1. 开场白:为什么要学 Asyncio?
🤔 先来个灵魂拷问
你是否遇到过这些情况:
- ❌ 写了个爬虫,爬1000个网页,喝了三杯咖啡还没爬完?
- ❌ 程序需要同时处理多个网络请求,结果卡得像PPT?
- ❌ 看着CPU使用率只有5%,心里默默流泪:"为啥不能跑满?"
- ❌ 用了多线程,结果发现代码比单线程还慢?
如果你点了头,恭喜你!🎉 你找对地方了!
💡 Asyncio 的三大超能力
- 高效并发 - 一个线程同时处理成千上万个任务
- 资源友好 - 不像多线程那样吃内存
- 代码优雅 - async/await 语法简洁明了
📊 一个震撼的对比
同步爬虫:爬100个网页 → 耗时 100秒 😴
异步爬虫:爬100个网页 → 耗时 5秒 🚀
差距:20倍!!!
看到没?这就是异步编程的魅力! ✨
2. 第一章:什么是异步编程?
🏪 生活中的同步 vs 异步
场景一:奶茶店的故事 🧋
同步模式(传统方式):
你:老板,我要一杯奶茶!
老板:好的,你等着!
【你站在那里傻等5分钟...】
【什么都不能干,只能刷手机...】
老板:你的奶茶好了!
你:谢谢!(心想:这5分钟我都能背10个单词了...)
异步模式(Asyncio方式):
你:老板,我要一杯奶茶!
老板:好的,等会叫你!
【你拿着号码牌去旁边坐下】
【你可以刷微博、看视频、回消息...】
老板:8号!你的奶茶好了!
你:来了!(心想:真爽,时间都没浪费!)
这就是异步的精髓:在等待的时候,去做其他事情! 🎯
场景二:餐厅大厨的智慧 👨🍳
想象你是一个大厨,需要做三道菜:
同步做法(笨蛋厨师):
1. 煮米饭(等30分钟) → 站着发呆...
2. 炒菜(等10分钟) → 继续发呆...
3. 煲汤(等20分钟) → 再次发呆...
总耗时:60分钟 😫
异步做法(聪明厨师):
1. 把米放进电饭煲(30分钟后熟)
2. 立即把汤放炉子上(20分钟后好)
3. 立即开始炒菜(10分钟搞定)
4. 炒完菜,汤也快好了
5. 汤好了,饭也熟了
总耗时:30分钟 🎉
效率提升:2倍!这就是异步的魔力! ⚡
📖 计算机世界的同步 vs 异步
同步执行(Synchronous)
# 同步代码示例
import time
def download_page(url):
print(f"开始下载:{url}")
time.sleep(2) # 模拟网络请求
print(f"下载完成:{url}")
return f"内容_{url}"
# 下载3个网页
start = time.time()
download_page("page1.com") # 等2秒
download_page("page2.com") # 再等2秒
download_page("page3.com") # 又等2秒
end = time.time()
print(f"总耗时:{end - start:.2f}秒") # 输出:6秒
执行流程图:
时间轴:→→→→→→→→→→→→→→→→→→→→
任务1:[■■■■■■]
任务2: [■■■■■■]
任务3: [■■■■■■]
0s 2s 4s 6s
异步执行(Asynchronous)
# 异步代码示例
import asyncio
async def download_page(url):
print(f"开始下载:{url}")
await asyncio.sleep(2) # 异步等待
print(f"下载完成:{url}")
return f"内容_{url}"
async def main():
start = time.time()
# 同时下载3个网页
await asyncio.gather(
download_page("page1.com"),
download_page("page2.com"),
download_page("page3.com")
)
end = time.time()
print(f"总耗时:{end - start:.2f}秒") # 输出:2秒
asyncio.run(main())
执行流程图:
时间轴:→→→→→→→→
任务1:[■■■■■■]
任务2:[■■■■■■]
任务3:[■■■■■■]
0s 2s
看到了吗?同样的任务,异步执行快了3倍! 🚀
3. 第二章:Asyncio 核心概念大揭秘
🎭 核心概念全家福
Asyncio 主要有4个核心概念:
- 协程(Coroutine) - 异步任务的基本单位
- 事件循环(Event Loop) - 任务调度器
- 任务(Task) - 协程的包装器
- Future - 异步操作的结果占位符
让我用最通俗的方式给你讲明白!👇
🎪 概念1:协程(Coroutine)
什么是协程?
官方定义:一种可以暂停和恢复执行的函数。
人话版本:一个可以中途停下来、等会儿再继续的函数。
生活中的协程
想象你在看电视剧:
正常函数:必须一口气看完整季(中间不能暂停)
协程函数:可以看一半,去吃个饭,回来继续看
协程 = 支持"暂停"和"恢复"的函数
代码示例
import asyncio
# 定义一个协程函数(注意 async 关键字)
async def make_coffee():
print("1. 烧水中... 💧")
await asyncio.sleep(1) # 暂停1秒(模拟烧水)
print("2. 磨咖啡豆中... ☕")
await asyncio.sleep(1) # 暂停1秒(模拟磨豆)
print("3. 冲泡中... 🫖")
await asyncio.sleep(1) # 暂停1秒(模拟冲泡)
print("4. 咖啡做好了!✅")
return "一杯香浓咖啡"
# 运行协程
result = asyncio.run(make_coffee())
print(f"享用:{result}")
输出:
1. 烧水中... 💧
2. 磨咖啡豆中... ☕
3. 冲泡中... 🫖
4. 咖啡做好了!✅
享用:一杯香浓咖啡
🔑 关键点
async def- 定义协程函数的关键字await- 暂停执行,等待结果- 协程函数调用后不会立即执行,需要用
asyncio.run()来运行
⚠️ 新手常犯的错误
# ❌ 错误1:忘记 await
async def wrong_example():
asyncio.sleep(1) # 这样不会等待!
print("立即打印")
# ✅ 正确做法
async def correct_example():
await asyncio.sleep(1) # 这样才会等待
print("1秒后打印")
# ❌ 错误2:直接调用协程函数
async def my_coro():
return "Hello"
result = my_coro() # 这样只会得到一个协程对象,不会执行
# ✅ 正确做法
result = asyncio.run(my_coro()) # 这样才会执行并得到结果
🎡 概念2:事件循环(Event Loop)
什么是事件循环?
官方定义:负责管理和调度异步任务执行的核心机制。
人话版本:一个超级管家,负责安排谁先做、谁后做。
生活中的事件循环
想象一个游乐场的旋转木马:
🎠 旋转木马(Event Loop)一直在转
🧒 小朋友们(Task)排队上去玩
📢 管理员(Event Loop)决定谁上谁下
过程:
1. 旋转木马一直转(循环运行)
2. 小朋友A上去玩一会儿
3. 小朋友A玩累了(await),下来休息
4. 小朋友B上去玩
5. 小朋友A休息好了,再次上去
6. 循环往复...
事件循环工作原理图
┌─────────────────────────────────────┐
│ 事件循环(Event Loop) │
│ │
│ ┌───────────────────────────────┐ │
│ │ 任务队列(Task Queue) │ │
│ │ │ │
│ │ [Task1] [Task2] [Task3] │ │
│ └───────────────────────────────┘ │
│ ↓ ↑ │
│ ┌─────────────────────┐ │
│ │ 正在执行的任务 │ │
│ │ await 时暂停 │ │
│ └─────────────────────┘ │
└─────────────────────────────────────┘
代码示例
import asyncio
async def task1():
print("任务1:开始")
await asyncio.sleep(2)
print("任务1:结束")
async def task2():
print("任务2:开始")
await asyncio.sleep(1)
print("任务2:结束")
async def main():
# 创建两个任务,让事件循环管理它们
await asyncio.gather(task1(), task2())
# asyncio.run() 会自动创建和管理事件循环
asyncio.run(main())
输出:
任务1:开始
任务2:开始
任务2:结束 # 1秒后
任务1:结束 # 2秒后
执行流程:
时间 事件循环在干什么
0s → 开始执行 task1,打印"任务1:开始"
→ task1 遇到 await,暂停,切换到 task2
→ 开始执行 task2,打印"任务2:开始"
→ task2 遇到 await,暂停
→ 事件循环等待...
1s → task2 的 sleep 结束,恢复执行
→ 打印"任务2:结束"
→ task2 完成
→ 事件循环继续等待...
2s → task1 的 sleep 结束,恢复执行
→ 打印"任务1:结束"
→ task1 完成
→ 所有任务完成,事件循环结束
🎯 事件循环的三种使用方式
# 方式1:推荐用法(Python 3.7+)
asyncio.run(main())
# 方式2:手动管理(了解即可)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
# 方式3:在已有循环中运行(高级用法)
loop = asyncio.get_running_loop()
loop.create_task(my_task())
建议:99% 的情况下,用 asyncio.run() 就够了! 👍
📦 概念3:任务(Task)
什么是任务?
官方定义:Task 是对协程的封装,用于在事件循环中并发执行。
人话版本:把协程包装成可以并发运行的任务。
生活中的任务
想象你是个项目经理:
协程 = 员工写好的工作计划文档
任务 = 把工作计划分配给员工,让他们真正开始干活
只有把计划(协程)变成任务(Task),员工才会开始工作!
代码示例
import asyncio
import time
async def say_after(delay, message):
await asyncio.sleep(delay)
print(message)
async def main_without_tasks():
"""不使用 Task:串行执行"""
print("=== 不使用 Task(慢) ===")
start = time.time()
await say_after(1, "Hello")
await say_after(2, "World")
print(f"耗时:{time.time() - start:.2f}秒\n")
async def main_with_tasks():
"""使用 Task:并发执行"""
print("=== 使用 Task(快) ===")
start = time.time()
# 创建任务(立即开始执行)
task1 = asyncio.create_task(say_after(1, "Hello"))
task2 = asyncio.create_task(say_after(2, "World"))
# 等待所有任务完成
await task1
await task2
print(f"耗时:{time.time() - start:.2f}秒\n")
# 运行对比
asyncio.run(main_without_tasks()) # 耗时 3秒
asyncio.run(main_with_tasks()) # 耗时 2秒
输出:
=== 不使用 Task(慢) ===
Hello
World
耗时:3.00秒
=== 使用 Task(快) ===
Hello
World
耗时:2.00秒
🔧 创建任务的几种方式
import asyncio
async def my_coro(name):
print(f"{name} 开始")
await asyncio.sleep(1)
print(f"{name} 结束")
return f"{name} 的结果"
async def main():
# 方式1:create_task(推荐)
task1 = asyncio.create_task(my_coro("任务1"))
# 方式2:ensure_future(兼容老版本)
task2 = asyncio.ensure_future(my_coro("任务2"))
# 方式3:gather(最常用,可以批量创建)
results = await asyncio.gather(
my_coro("任务3"),
my_coro("任务4")
)
# 等待 task1 和 task2
result1 = await task1
result2 = await task2
print(f"结果:{result1}, {result2}, {results}")
asyncio.run(main())
📊 Task vs 直接 await 协程
| 特性 | 直接 await | 创建 Task |
|---|---|---|
| 执行时机 | 遇到 await 才开始 | 立即开始 |
| 并发性 | 串行执行 | 并发执行 |
| 性能 | 慢 ⏱️ | 快 🚀 |
| 使用场景 | 需要等待单个结果 | 需要并发多个任务 |
🔮 概念4:Future(了解即可)
什么是 Future?
官方定义:代表一个异步操作的最终结果的占位符。
人话版本:一个"欠条",承诺将来会给你结果。
生活中的 Future
你:老板,我要一杯咖啡
老板:给你一个号码牌(这就是 Future)
你拿着号码牌(Future),知道将来能换到咖啡
等咖啡做好,号码牌(Future)就可以换成真咖啡了
代码示例
import asyncio
async def main():
# 获取当前事件循环
loop = asyncio.get_running_loop()
# 创建一个 Future 对象(欠条)
future = loop.create_future()
async def set_result():
await asyncio.sleep(1)
# 1秒后,兑现承诺
future.set_result("这是结果!")
# 启动任务
asyncio.create_task(set_result())
# 等待 Future 完成
result = await future
print(f"得到结果:{result}")
asyncio.run(main())
注意:在实际编程中,你很少需要直接操作 Future,Task 已经够用了!
🎨 四大概念关系图
┌─────────────────────────────────────────────────────┐
│ 你的异步程序 │
├─────────────────────────────────────────────────────┤
│ │
│ async def my_func(): ← 协程函数 │
│ await asyncio.sleep(1) │
│ ↓ │
│ my_func() 返回协程对象 ← 协程(Coroutine) │
│ ↓ │
│ asyncio.create_task(...) ← 包装成任务(Task) │
│ ↓ │
│ Event Loop ← 事件循环调度执行 │
│ / | \ │
│ Task1 Task2 Task3 │
│ ↓ ↓ ↓ │
│ Future Future Future ← 底层实现 │
│ │
└─────────────────────────────────────────────────────┘
4. 第三章:实战演练 - 从入门到精通
🎯 实战1:第一个异步程序
import asyncio
async def hello_world():
"""最简单的异步函数"""
print("Hello")
await asyncio.sleep(1) # 异步等待1秒
print("World")
# 运行
asyncio.run(hello_world())
知识点:
async def定义异步函数await等待异步操作asyncio.run()运行异步函数
🎯 实战2:并发执行多个任务
import asyncio
import time
async def brew_coffee():
"""煮咖啡"""
print("☕ 开始煮咖啡...")
await asyncio.sleep(3)
print("☕ 咖啡煮好了!")
return "咖啡"
async def toast_bread():
"""烤面包"""
print("🍞 开始烤面包...")
await asyncio.sleep(2)
print("🍞 面包烤好了!")
return "面包"
async def fry_eggs():
"""煎鸡蛋"""
print("🍳 开始煎鸡蛋...")
await asyncio.sleep(1)
print("🍳 鸡蛋煎好了!")
return "鸡蛋"
async def make_breakfast():
"""做早餐"""
print("=== 开始做早餐 ===\n")
start = time.time()
# 并发执行三个任务
results = await asyncio.gather(
brew_coffee(),
toast_bread(),
fry_eggs()
)
elapsed = time.time() - start
print(f"\n=== 早餐做好了!===")
print(f"菜单:{', '.join(results)}")
print(f"总耗时:{elapsed:.2f}秒")
print(f"如果串行执行需要:3+2+1=6秒")
print(f"效率提升:{6/elapsed:.1f}倍!🚀")
asyncio.run(make_breakfast())
输出:
=== 开始做早餐 ===
☕ 开始煮咖啡...
🍞 开始烤面包...
🍳 开始煎鸡蛋...
🍳 鸡蛋煎好了!
🍞 面包烤好了!
☕ 咖啡煮好了!
=== 早餐做好了!===
菜单:咖啡, 面包, 鸡蛋
总耗时:3.00秒
如果串行执行需要:3+2+1=6秒
效率提升:2.0倍!🚀
🎯 实战3:异步网络爬虫
import asyncio
import aiohttp # 需要安装:pip install aiohttp
import time
async def fetch_url(session, url):
"""异步获取网页内容"""
print(f"🌐 开始抓取:{url}")
async with session.get(url) as response:
content = await response.text()
print(f"✅ 完成抓取:{url}(长度:{len(content)}字符)")
return url, len(content)
async def fetch_all(urls):
"""并发抓取多个网页"""
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
return results
async def main():
urls = [
"http://www.example.com",
"http://www.python.org",
"http://www.github.com",
"http://www.stackoverflow.com",
"http://www.wikipedia.org",
]
print("=== 异步爬虫开始 ===\n")
start = time.time()
results = await fetch_all(urls)
elapsed = time.time() - start
print(f"\n=== 爬取完成 ===")
print(f"抓取了 {len(results)} 个网页")
print(f"总耗时:{elapsed:.2f}秒")
print("如果用同步方式,至少需要 10-20秒!")
# 运行
asyncio.run(main())
需要安装的库:
pip install aiohttp
🎯 实战4:异步文件操作
import asyncio
import aiofiles # 需要安装:pip install aiofiles
async def write_file(filename, content):
"""异步写文件"""
async with aiofiles.open(filename, 'w', encoding='utf-8') as f:
await f.write(content)
print(f"✅ 写入完成:{filename}")
async def read_file(filename):
"""异步读文件"""
async with aiofiles.open(filename, 'r', encoding='utf-8') as f:
content = await f.read()
print(f"✅ 读取完成:{filename}({len(content)}字符)")
return content
async def main():
# 并发写入多个文件
await asyncio.gather(
write_file("file1.txt", "这是文件1的内容"),
write_file("file2.txt", "这是文件2的内容"),
write_file("file3.txt", "这是文件3的内容"),
)
# 并发读取多个文件
contents = await asyncio.gather(
read_file("file1.txt"),
read_file("file2.txt"),
read_file("file3.txt"),
)
print(f"\n读取到 {len(contents)} 个文件")
asyncio.run(main())
需要安装的库:
pip install aiofiles
🎯 实战5:超时控制
import asyncio
async def slow_task():
"""一个很慢的任务"""
print("开始执行慢任务...")
await asyncio.sleep(10)
print("慢任务完成!")
return "结果"
async def main():
try:
# 设置3秒超时
result = await asyncio.wait_for(slow_task(), timeout=3.0)
print(f"结果:{result}")
except asyncio.TimeoutError:
print("⏰ 任务超时了!")
asyncio.run(main())
输出:
开始执行慢任务...
⏰ 任务超时了!
🎯 实战6:任务取消
import asyncio
async def long_task():
"""一个长时间运行的任务"""
try:
print("任务开始...")
await asyncio.sleep(100)
print("任务完成!")
except asyncio.CancelledError:
print("❌ 任务被取消了!")
raise # 重要:必须重新抛出
async def main():
# 创建任务
task = asyncio.create_task(long_task())
# 等待1秒
await asyncio.sleep(1)
# 取消任务
task.cancel()
try:
await task
except asyncio.CancelledError:
print("✅ 已确认任务被取消")
asyncio.run(main())
输出:
任务开始...
❌ 任务被取消了!
✅ 已确认任务被取消
🎯 实战7:生产者-消费者模式
import asyncio
import random
async def producer(queue, producer_id):
"""生产者:生产商品"""
for i in range(5):
item = f"商品{producer_id}-{i}"
await queue.put(item)
print(f"🏭 生产者{producer_id} 生产了:{item}")
await asyncio.sleep(random.uniform(0.1, 0.5))
print(f"🏭 生产者{producer_id} 完成工作")
async def consumer(queue, consumer_id):
"""消费者:消费商品"""
while True:
item = await queue.get()
print(f"🛒 消费者{consumer_id} 消费了:{item}")
await asyncio.sleep(random.uniform(0.2, 0.8))
queue.task_done()
async def main():
# 创建队列
queue = asyncio.Queue(maxsize=10)
# 创建2个生产者
producers = [
asyncio.create_task(producer(queue, 1)),
asyncio.create_task(producer(queue, 2))
]
# 创建3个消费者
consumers = [
asyncio.create_task(consumer(queue, 1)),
asyncio.create_task(consumer(queue, 2)),
asyncio.create_task(consumer(queue, 3))
]
# 等待所有生产者完成
await asyncio.gather(*producers)
# 等待队列中的所有任务被处理
await queue.join()
# 取消所有消费者
for c in consumers:
c.cancel()
asyncio.run(main())
🎯 实战8:限制并发数量
import asyncio
async def download(semaphore, url_id):
"""模拟下载"""
async with semaphore: # 获取信号量
print(f"⬇️ 开始下载 {url_id}")
await asyncio.sleep(2)
print(f"✅ 完成下载 {url_id}")
async def main():
# 限制同时只能有3个任务执行
semaphore = asyncio.Semaphore(3)
# 创建10个下载任务
tasks = [
download(semaphore, i)
for i in range(10)
]
await asyncio.gather(*tasks)
asyncio.run(main())
效果:虽然有10个任务,但同一时刻最多只有3个在执行。
5. 第四章:常见坑点与最佳实践
⚠️ 坑点1:忘记使用 await
# ❌ 错误示例
async def wrong():
asyncio.sleep(1) # 忘记 await,不会等待!
print("立即打印")
# ✅ 正确示例
async def correct():
await asyncio.sleep(1) # 记得 await
print("1秒后打印")
记忆口诀:调用异步函数,await 不能少!
⚠️ 坑点2:在同步函数中调用异步函数
# ❌ 错误示例
def sync_function():
await some_async_function() # SyntaxError!
# ✅ 正确示例1:改成异步函数
async def async_function():
await some_async_function()
# ✅ 正确示例2:使用 asyncio.run
def sync_function():
asyncio.run(some_async_function())
⚠️ 坑点3:阻塞操作没有使用异步版本
import time
import asyncio
# ❌ 错误示例:使用了同步的 sleep
async def bad_example():
time.sleep(1) # 会阻塞整个事件循环!
# ✅ 正确示例:使用异步的 sleep
async def good_example():
await asyncio.sleep(1) # 不会阻塞
常见的同步操作替换:
| 同步版本 | 异步版本 | 备注 |
|---|---|---|
time.sleep() | asyncio.sleep() | 延时 |
requests.get() | aiohttp.get() | HTTP请求 |
open() | aiofiles.open() | 文件操作 |
pymysql | aiomysql | MySQL |
redis | aioredis | Redis |
⚠️ 坑点4:没有正确处理异常
import asyncio
# ❌ 错误示例:异常被吞掉了
async def may_fail():
raise Exception("出错了!")
async def bad_example():
task = asyncio.create_task(may_fail())
# 如果不 await,异常不会被捕获!
# task 会默默失败
# ✅ 正确示例1:await 任务
async def good_example1():
task = asyncio.create_task(may_fail())
try:
await task
except Exception as e:
print(f"捕获到异常:{e}")
# ✅ 正确示例2:使用 gather 的 return_exceptions
async def good_example2():
results = await asyncio.gather(
may_fail(),
return_exceptions=True # 返回异常而不是抛出
)
for r in results:
if isinstance(r, Exception):
print(f"任务失败:{r}")
⚠️ 坑点5:在循环中串行执行
# ❌ 错误示例:看起来是异步,实际是串行
async def bad_example():
for i in range(10):
await asyncio.sleep(1) # 串行执行,总共10秒
# ✅ 正确示例:真正的并发
async def good_example():
tasks = [asyncio.sleep(1) for i in range(10)]
await asyncio.gather(*tasks) # 并发执行,总共1秒
🏆 最佳实践
1. 使用 asyncio.run() 作为入口
# ✅ 推荐
async def main():
# 你的异步代码
pass
if __name__ == "__main__":
asyncio.run(main())
2. 使用 gather 并发执行多个任务
# ✅ 推荐
async def main():
results = await asyncio.gather(
task1(),
task2(),
task3()
)
3. 使用 create_task 提前启动任务
# ✅ 推荐:任务会立即开始
async def main():
task1 = asyncio.create_task(long_task1())
task2 = asyncio.create_task(long_task2())
# 可以做其他事情
# 最后等待结果
result1 = await task1
result2 = await task2
4. 使用上下文管理器
# ✅ 推荐
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
data = await response.json()
5. 设置超时
# ✅ 推荐
try:
result = await asyncio.wait_for(
slow_operation(),
timeout=5.0
)
except asyncio.TimeoutError:
print("操作超时")
6. 第五章:性能对比与实战应用
📊 性能对比实验
让我们做一个实验,对比同步和异步的性能差异:
import asyncio
import aiohttp
import requests
import time
# 同步版本
def sync_fetch(url):
response = requests.get(url)
return len(response.text)
def sync_main(urls):
start = time.time()
results = [sync_fetch(url) for url in urls]
elapsed = time.time() - start
print(f"同步方式:{elapsed:.2f}秒")
return results
# 异步版本
async def async_fetch(session, url):
async with session.get(url) as response:
text = await response.text()
return len(text)
async def async_main(urls):
start = time.time()
async with aiohttp.ClientSession() as session:
tasks = [async_fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks)
elapsed = time.time() - start
print(f"异步方式:{elapsed:.2f}秒")
return results
# 测试
urls = [
"http://www.example.com",
"http://www.python.org",
"http://www.github.com",
] * 10 # 30个URL
print("=== 性能对比 ===\n")
sync_main(urls)
asyncio.run(async_main(urls))
典型输出:
=== 性能对比 ===
同步方式:45.23秒
异步方式:2.34秒
性能提升:19倍!🚀
🌟 实战应用场景
场景1:批量API调用
import asyncio
import aiohttp
async def call_api(session, api_url, user_id):
"""调用API获取用户信息"""
url = f"{api_url}/users/{user_id}"
async with session.get(url) as response:
return await response.json()
async def batch_get_users(api_url, user_ids):
"""批量获取用户信息"""
async with aiohttp.ClientSession() as session:
tasks = [
call_api(session, api_url, uid)
for uid in user_ids
]
users = await asyncio.gather(*tasks)
return users
# 使用
user_ids = range(1, 101) # 100个用户
users = asyncio.run(
batch_get_users("https://api.example.com", user_ids)
)
场景2:实时数据聚合
import asyncio
async def get_stock_price(symbol):
"""获取股票价格"""
# 模拟API调用
await asyncio.sleep(0.5)
return {symbol: 123.45}
async def get_crypto_price(symbol):
"""获取加密货币价格"""
await asyncio.sleep(0.3)
return {symbol: 50000}
async def get_forex_rate(pair):
"""获取外汇汇率"""
await asyncio.sleep(0.4)
return {pair: 6.5}
async def get_dashboard_data():
"""获取仪表盘数据"""
# 并发获取所有数据
stock, crypto, forex = await asyncio.gather(
get_stock_price("AAPL"),
get_crypto_price("BTC"),
get_forex_rate("USD/CNY")
)
return {
"stock": stock,
"crypto": crypto,
"forex": forex
}
# 使用
data = asyncio.run(get_dashboard_data())
print(data)
# 耗时:0.5秒(最慢的那个)
# 如果串行:0.5 + 0.3 + 0.4 = 1.2秒
场景3:WebSocket 服务器
import asyncio
import websockets
# 存储所有连接的客户端
CLIENTS = set()
async def handler(websocket):
"""处理单个客户端连接"""
# 注册客户端
CLIENTS.add(websocket)
try:
async for message in websocket:
# 收到消息,广播给所有客户端
await broadcast(message)
finally:
# 移除客户端
CLIENTS.remove(websocket)
async def broadcast(message):
"""广播消息给所有客户端"""
if CLIENTS:
await asyncio.gather(
*[client.send(message) for client in CLIENTS]
)
async def main():
# 启动WebSocket服务器
async with websockets.serve(handler, "localhost", 8765):
print("WebSocket服务器启动在 ws://localhost:8765")
await asyncio.Future() # 永久运行
# 运行
asyncio.run(main())
场景4:数据库批量操作
import asyncio
import aiomysql
async def insert_user(pool, name, email):
"""插入用户"""
async with pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(
"INSERT INTO users (name, email) VALUES (%s, %s)",
(name, email)
)
await conn.commit()
async def batch_insert_users(users):
"""批量插入用户"""
# 创建连接池
pool = await aiomysql.create_pool(
host='localhost',
user='root',
password='password',
db='mydb',
minsize=5,
maxsize=10
)
try:
# 并发插入
tasks = [
insert_user(pool, user['name'], user['email'])
for user in users
]
await asyncio.gather(*tasks)
finally:
pool.close()
await pool.wait_closed()
# 使用
users = [
{"name": "张三", "email": "zhangsan@example.com"},
{"name": "李四", "email": "lisi@example.com"},
# ... 更多用户
]
asyncio.run(batch_insert_users(users))
🎓 进阶技巧
技巧1:限流(Rate Limiting)
import asyncio
import time
class RateLimiter:
"""限流器:每秒最多执行 N 次"""
def __init__(self, rate):
self.rate = rate # 每秒允许的次数
self.tokens = rate
self.last_update = time.time()
self.lock = asyncio.Lock()
async def acquire(self):
"""获取令牌"""
async with self.lock:
now = time.time()
# 补充令牌
elapsed = now - self.last_update
self.tokens = min(
self.rate,
self.tokens + elapsed * self.rate
)
self.last_update = now
# 等待直到有令牌
while self.tokens < 1:
await asyncio.sleep(0.1)
now = time.time()
elapsed = now - self.last_update
self.tokens += elapsed * self.rate
self.last_update = now
self.tokens -= 1
async def call_api(limiter, api_id):
"""调用API(带限流)"""
await limiter.acquire()
print(f"调用API {api_id}")
await asyncio.sleep(0.1)
async def main():
# 每秒最多5次
limiter = RateLimiter(5)
# 尝试调用20次
tasks = [call_api(limiter, i) for i in range(20)]
await asyncio.gather(*tasks)
asyncio.run(main())
技巧2:重试机制
import asyncio
import random
async def retry(coro_func, max_retries=3, delay=1):
"""重试装饰器"""
for attempt in range(max_retries):
try:
return await coro_func()
except Exception as e:
if attempt == max_retries - 1:
raise
print(f"失败,{delay}秒后重试... (尝试 {attempt + 1}/{max_retries})")
await asyncio.sleep(delay)
async def unstable_api():
"""不稳定的API(50%失败率)"""
if random.random() < 0.5:
raise Exception("API调用失败")
return "成功!"
async def main():
result = await retry(unstable_api, max_retries=5)
print(f"最终结果:{result}")
asyncio.run(main())
技巧3:缓存
import asyncio
import time
from functools import wraps
def async_cache(ttl=60):
"""异步缓存装饰器"""
cache = {}
def decorator(func):
@wraps(func)
async def wrapper(*args):
key = args
now = time.time()
# 检查缓存
if key in cache:
value, timestamp = cache[key]
if now - timestamp < ttl:
print(f"缓存命中:{key}")
return value
# 调用函数
result = await func(*args)
cache[key] = (result, now)
return result
return wrapper
return decorator
@async_cache(ttl=10)
async def expensive_operation(x):
"""耗时操作"""
print(f"执行耗时操作:{x}")
await asyncio.sleep(2)
return x * 2
async def main():
print("第一次调用:")
result1 = await expensive_operation(5)
print(f"结果:{result1}\n")
print("第二次调用(应该命中缓存):")
result2 = await expensive_operation(5)
print(f"结果:{result2}\n")
asyncio.run(main())
7. 总结:你已经是异步编程高手了!
🎉 恭喜你!
如果你看到这里,说明你已经掌握了 Python Asyncio 的精髓!让我们回顾一下你学到的内容:
✅ 核心概念
- ✨ 协程(Coroutine):可暂停和恢复的函数
- ✨ 事件循环(Event Loop):任务调度中心
- ✨ 任务(Task):并发执行的单位
- ✨ Future:异步操作的结果占位符
✅ 关键语法
async def- 定义异步函数await- 等待异步操作asyncio.run()- 运行异步函数asyncio.create_task()- 创建任务asyncio.gather()- 并发执行多个任务
✅ 实战技能
- 🚀 并发网络请求
- 📁 异步文件操作
- ⏰ 超时控制
- ❌ 异常处理
- 🔄 生产者-消费者模式
- 🎚️ 并发数量控制
✅ 最佳实践
- 用
asyncio.run()作为入口 - 用
gather并发多个任务 - 用
create_task提前启动任务 - 正确处理异常
- 避免阻塞操作
🎯 学习路线建议
初级阶段(已完成 ✅)
- 理解异步编程基本概念
- 掌握 async/await 语法
- 能写简单的异步程序
中级阶段(继续努力 💪)
- 熟练使用 aiohttp、aiofiles 等库
- 理解事件循环的工作原理
- 掌握异步异常处理
高级阶段(进阶目标 🎓)
- 自己实现异步上下文管理器
- 理解协程的底层实现
- 性能优化和调优
📚 推荐资源
官方文档
常用异步库
- aiohttp - 异步HTTP客户端/服务器
- aiofiles - 异步文件操作
- aiomysql - 异步MySQL驱动
- motor - 异步MongoDB驱动
- asyncpg - 异步PostgreSQL驱动
- websockets - WebSocket支持
学习建议
- 多动手:每个例子都自己敲一遍
- 多实践:用异步重写你的旧项目
- 多思考:理解什么时候该用异步,什么时候不该用
- 多调试:学会使用调试工具
🤔 常见问题 FAQ
Q1:什么时候应该用异步?
A1:
- ✅ I/O密集型任务(网络请求、文件读写、数据库操作)
- ✅ 需要高并发的场景
- ❌ CPU密集型任务(建议用多进程)
- ❌ 简单的脚本(过度设计)
Q2:异步一定比同步快吗?
A2: 不一定!
- 异步适合I/O密集型任务
- CPU密集型任务用异步可能更慢
- 任务数量少时优势不明显
Q3:asyncio vs 多线程 vs 多进程?
A3:
asyncio: 单线程,适合I/O密集型,资源占用少
多线程: 多线程,GIL限制,适合I/O密集型
多进程: 多进程,适合CPU密集型,资源占用多
Q4:如何调试异步代码?
A4:
# 1. 启用调试模式
asyncio.run(main(), debug=True)
# 2. 使用日志
import logging
logging.basicConfig(level=logging.DEBUG)
# 3. 使用 asyncio 的调试工具
loop = asyncio.get_event_loop()
loop.set_debug(True)
Q5:能在一个程序中混用同步和异步吗?
A5: 可以,但要注意:
- 在异步函数中调用同步阻塞操作会阻塞整个事件循环
- 可以用
run_in_executor在线程池中运行同步代码
import asyncio
def blocking_operation():
# 耗时的同步操作
time.sleep(2)
return "结果"
async def main():
loop = asyncio.get_event_loop()
# 在线程池中运行同步函数
result = await loop.run_in_executor(
None,
blocking_operation
)
print(result)
💪 练习题
想检验一下学习成果吗?试试这些练习:
练习1:异步倒计时
编写一个程序,同时启动3个倒计时器,分别从3、5、7开始倒数。
练习2:并发下载器
编写一个并发下载器,同时下载10张图片,并显示进度。
练习3:聊天室
用 asyncio 实现一个简单的聊天室服务器,支持多个客户端同时连接。
练习4:数据采集器
编写一个程序,并发采集多个网站的标题,并保存到文件。
🌟 最后的话
异步编程就像学骑自行车:
- 刚开始觉得很难 😰
- 练习一段时间后开始有感觉 😊
- 熟练后就成为本能 😎
- 最后发现:这么简单,早该学了!🎉
记住:
- 不要怕出错 - 错误是最好的老师
- 多写代码 - 实践出真知
- 保持好奇心 - 技术永远在进步
🎁 彩蛋:一个完整的实战项目
"""
异步新闻聚合器
功能:并发抓取多个新闻网站的头条,按时间排序后展示
"""
import asyncio
import aiohttp
from datetime import datetime
from bs4 import BeautifulSoup # 需要安装:pip install beautifulsoup4
class NewsAggregator:
"""新闻聚合器"""
def __init__(self):
self.news = []
async def fetch_news(self, session, site_name, url, selector):
"""抓取单个网站的新闻"""
try:
print(f"📰 正在抓取 {site_name}...")
async with session.get(url, timeout=10) as response:
html = await response.text()
soup = BeautifulSoup(html, 'html.parser')
# 提取新闻标题
titles = soup.select(selector)
for title in titles[:5]: # 只取前5条
self.news.append({
'source': site_name,
'title': title.get_text().strip(),
'time': datetime.now()
})
print(f"✅ {site_name} 抓取完成")
except Exception as e:
print(f"❌ {site_name} 抓取失败:{e}")
async def aggregate(self, sites):
"""聚合多个网站的新闻"""
async with aiohttp.ClientSession() as session:
tasks = [
self.fetch_news(session, name, url, selector)
for name, url, selector in sites
]
await asyncio.gather(*tasks)
# 按时间排序
self.news.sort(key=lambda x: x['time'], reverse=True)
return self.news
def display(self):
"""显示新闻"""
print("\n" + "="*60)
print(" "*20 + "📰 今日头条 📰")
print("="*60 + "\n")
for i, item in enumerate(self.news, 1):
print(f"{i}. [{item['source']}] {item['title']}")
print("\n" + "="*60)
print(f"共聚合了 {len(self.news)} 条新闻")
print("="*60)
async def main():
# 配置新闻网站
sites = [
("示例网站1", "https://example.com", "h2.title"),
("示例网站2", "https://example.org", "div.news-title"),
# 添加更多网站...
]
# 创建聚合器
aggregator = NewsAggregator()
# 开始聚合
start = datetime.now()
await aggregator.aggregate(sites)
elapsed = (datetime.now() - start).total_seconds()
# 显示结果
aggregator.display()
print(f"\n⏱️ 总耗时:{elapsed:.2f}秒")
if __name__ == "__main__":
asyncio.run(main())
🎊 结语
恭喜你完成了这趟 Asyncio 学习之旅!🎓
现在的你已经具备了:
- ✅ 异步编程的理论基础
- ✅ 实战开发的能力
- ✅ 解决问题的思路
下一步:
- 把学到的知识应用到实际项目中
- 尝试优化你现有的代码
- 分享给你的朋友,一起进步!
记住:编程是一门手艺,熟能生巧!💪
"异步编程不是魔法,只是一种更聪明的工作方式。" ✨
祝你在异步编程的道路上越走越远!🚀
—— 你最贴心的技术小助手 😊
📮 反馈与交流
如果这份文档帮助到了你,别忘了:
- ⭐ 点个星星
- 💬 留言分享你的学习心得
- 🐛 发现错误?欢迎指正!
Happy Coding! 🎉
最后更新时间:2024年 版本:v1.0 作者:你最贴心的技术小助手
声明:本文档仅供学习交流使用,示例代码已经过测试,但在生产环境使用前请充分测试。