3.6 KiB
3.6 KiB
协程
协程(Coroutine)是用户态的轻量级线程,由程序自身控制调度,通过协作式多任务实现并发。它能在单线程内挂起和恢复执行,无需操作系统介入,切换开销极小,尤其适合 I/O 密集型任务(如网络请求、文件读写)。
与线程/进程对比:
- 资源消耗:协程内存占用更低(共享进程内存),线程需独立栈空间,进程资源消耗最大
- 切换开销:协程切换在用户态完成,速度极快;线程/进程切换依赖操作系统,开销较大
- 适用场景:协程适合高并发 I/O 操作;线程适合 CPU 密集型任务;进程适合多核并行计算
核心优势:
- 高并发:单线程可处理数千级并发连接(如 Web 服务器)
- 无锁机制:避免多线程同步问题(如死锁、竞态条件)
- 代码简洁:用同步语法写异步逻辑,避免回调地狱
实现方式
生成器函数
通过 yield 暂停执行并传递值:需要手动管理状态,适用简单场景
def simple_coroutine():
print("协程启动")
x = yield # 暂停点,等待外部传入值
print(f"接收值: {x}")
coro = simple_coroutine()
next(coro) # 启动协程,执行到第一个 yield
coro.send(10) # 恢复执行,x 赋值为 10
async/await
通过 asyncio 库实现异步编程
import asyncio
async def fetch_data(url):
print(f"请求 {url}")
await asyncio.sleep(1) # 挂起协程,让出控制权给事件循环,模拟异步等待
return f"来自 {url} 的数据"
async def main():
tasks = [fetch_data("url1"), fetch_data("url2")]
results = await asyncio.gather(*tasks) # 实现多任务并发调度:并发执行
print(results)
if __name__ == "__main__":
asyncio.run(main())
基本语法
定义协程函数
使用 async def
声明协程函数
import asyncio
async def my_coroutine():
print("协程开始")
await asyncio.sleep(1) # 模拟 I/O 操作
print("协程结束")
运行协程
协程需要通过事件循环执行
async def main():
await my_coroutine() # 等待协程完成
if __name__ == "__main__":
asyncio.run(main())
事件循环
事件循环是协程的调度核心,负责执行、切换和监控协程任务
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()) # 并发执行多个协程
if __name__ == "__main__":
asyncio.run(main())
进阶用法
任务
将协程封装为任务,更灵活地控制执行
async def main():
task = asyncio.create_task(my_coroutine()) # 创建任务
await task # 等待任务完成
超时控制
设置协程执行的超时时间
async def slow_task():
await asyncio.sleep(10)
async def main():
try:
await asyncio.wait_for(slow_task(), timeout=3)
except asyncio.TimeoutError:
print("任务超时")
协程同步
使用锁 Lock
保护共享资源
lock = asyncio.Lock()
async def safe_write():
async with lock: # 异步上下文管理器
# 安全地操作共享资源
pass
课后作业
- [必须] 动手完成本章节案例
- [扩展] 阅读官方文档相关章节
- [扩展] 用协程实现进程章节的爬虫案例