一、什么是协程?
协程(Coroutine)就像可以暂停执行的函数,能够在执行过程中主动让出控制权,等准备好后再继续执行。
生活小例子
想象你在咖啡店排队:
- 普通函数:必须一直排到取餐(阻塞等待)
- 协程:下单后去旁边座位等,轮到你再回来取(非阻塞)
二、快速入门
1. 最简单的协程
python
import asyncio
async def hello():
print("开始")
await asyncio.sleep(1) # 暂停1秒
print("结束")
asyncio.run(hello()) # 运行协程
2. 并发执行多个协程
python
async def make_coffee(name, time):
print(f"{name}开始制作")
await asyncio.sleep(time)
print(f"{name}制作完成")
async def main():
# 同时制作三杯咖啡
await asyncio.gather(
make_coffee("拿铁", 2),
make_coffee("美式", 1),
make_coffee("卡布", 3)
)
asyncio.run(main())
输出顺序:美式 → 拿铁 → 卡布(总耗时3秒)
三、核心概念
1. 关键字解析
关键字 | 作用说明 | 示例 |
---|---|---|
async | 定义协程函数 | async def func(): |
await | 暂停等待异步操作 | await task() |
run() | 启动协程的主入口 | asyncio.run(main()) |
2. 协程 vs 多线程
协程 | 多线程 | |
---|---|---|
内存占用 | 约1KB/任务 | 约8MB/线程 |
切换速度 | 100纳秒级 | 1微秒级 |
适用场景 | I/O密集型任务 | CPU密集型任务 |
四、实战应用
1. 网络请求并发
python
import aiohttp
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
urls = ["url1", "url2", "url3"]
results = await asyncio.gather(*[fetch(url) for url in urls])
print(f"获取到{len(results)}个结果")
asyncio.run(main())
2. 生产者-消费者模式
python
async def producer(queue):
for i in range(5):
await queue.put(i)
print(f"生产产品{i}")
await asyncio.sleep(0.5)
async def consumer(queue):
while True:
item = await queue.get()
print(f"消费产品{item}")
queue.task_done()
async def main():
queue = asyncio.Queue(3) # 最大容量3
await asyncio.gather(
producer(queue),
consumer(queue)
)
asyncio.run(main())
五、常见问题
1. 为什么我的协程不执行?
- 忘记使用
await
调用协程 - 没有通过
asyncio.run()
启动 - 在普通函数中调用协程
2. 如何停止无限循环的协程?
python
task = asyncio.create_task(infinite_task())
await asyncio.sleep(5)
task.cancel() # 5秒后取消任务
3. 协程会替代多线程吗?
- 适合:网络请求、文件IO、Web服务等I/O密集型场景
- 不适合:科学计算、图像处理等CPU密集型任务
六、优化
- 避免阻塞操作 :用
await asyncio.sleep()
代替time.sleep()
- 限制并发量:
python
sem = asyncio.Semaphore(10) # 最多同时10个
async def limited_task():
async with sem:
await heavy_work()
- 使用结构化并发(Python 3.11+):
python
async with asyncio.TaskGroup() as tg:
tg.create_task(task1())
tg.create_task(task2())
备注
个人水平有限,有问题随时交流~