Python中的协程(Coroutine)与异步编程
字数 1211 2025-11-23 01:35:35
Python中的协程(Coroutine)与异步编程
描述
协程是Python异步编程的核心概念,它允许函数在执行过程中暂停和恢复,从而高效处理I/O密集型任务。与多线程不同,协程在单线程内通过任务切换实现并发,避免了线程切换的开销和竞争条件问题。Python通过async/await语法和asyncio库原生支持协程。
详细讲解
1. 协程的基本概念
- 定义:协程是一种特殊的函数,可以在执行过程中暂停(
await)并将控制权交还事件循环,待某个操作(如I/O)完成后恢复执行。 - 优势:相比多线程,协程的上下文切换更轻量,且无需加锁(因为单线程内串行执行协程代码块)。
- 关键语法:
async def:声明一个异步函数(协程函数)。await:在协程内部暂停,等待异步操作完成。
2. 创建和运行协程
-
示例1:定义协程函数
async def simple_coroutine(): print("Start coroutine") await asyncio.sleep(1) # 模拟I/O操作,暂停1秒 print("Resume after 1 second")- 注意:直接调用
simple_coroutine()不会执行其内部代码,而是返回一个协程对象。
- 注意:直接调用
-
示例2:通过事件循环运行协程
import asyncio async def main(): await simple_coroutine() # 在协程中调用另一个协程 asyncio.run(main()) # 启动事件循环asyncio.run()是Python 3.7+推荐的入口点,负责管理事件循环。
3. 并发执行多个协程
- 问题:若按顺序
await多个协程,会串行执行(如先等协程A完成再执行B)。 - 解决方案:使用
asyncio.gather()或asyncio.create_task()实现并发。async def task(name, delay): print(f"Task {name} started") await asyncio.sleep(delay) print(f"Task {name} finished") async def main(): # 方案1:gather同时启动多个协程 await asyncio.gather( task("A", 2), task("B", 1) # B先完成 ) # 输出:A开始 -> B开始 -> B结束 -> A结束 # 方案2:create_task创建后台任务 task_a = asyncio.create_task(task("A", 2)) task_b = asyncio.create_task(task("B", 1)) await task_a # 等待A完成(B可能已先完成) await task_b
4. 协程的工作原理
- 事件循环(Event Loop):
- 核心调度器,维护一个任务队列(协程集合)。
- 当协程遇到
await时,事件循环暂停该协程,转去执行其他可运行的任务。 - 通过轮询I/O事件(如文件读写、网络请求完成)来唤醒等待中的协程。
- 状态切换:
- 协程有三种状态:
PENDING(等待执行)、RUNNING(运行中)、DONE(完成)。 await触发状态从RUNNING转为PENDING,恢复时转为RUNNING。
- 协程有三种状态:
5. 实际应用场景
- 高性能Web服务器:使用
aiohttp库处理大量并发请求。 - 数据库异步操作:如
asyncpg支持PostgreSQL的异步查询。 - 爬虫并发下载:同时发起多个网络请求而不阻塞主线程。
6. 常见误区与注意事项
- 避免阻塞操作:协程内不要调用同步I/O函数(如
time.sleep()),应使用异步版本(asyncio.sleep())。 - 错误处理:使用
try/except捕获协程内的异常,或通过gather(return_exceptions=True)收集异常。 - 资源管理:用异步上下文管理器(
async with)确保资源(如数据库连接)正确释放。
总结
协程通过async/await将异步代码写得像同步代码一样直观,结合事件循环实现高效并发。掌握协程的创建、调度和错误处理是构建高性能Python应用的关键。