Python中的协程(Coroutine)
Python中的协程(Coroutine) 是一种轻量级的异步执行单元,主要用于解决IO密集型任务的性能问题。Python 3.5引入了 async/await
语法,使得协程变得简洁且易于使用。协程的核心是通过事件循环(Event Loop) 来调度任务,在等待外部操作(如网络请求、文件读写)时,自动切换到其他任务,从而提升程序的整体效率。
一、协程的基本概念
-
协程(Coroutine)
- 是一个暂停和恢复执行的函数,通过
async def
定义。 - 协程不会阻塞整个事件循环,而是通过
await
关键字主动让出控制权。 - 协程本身不会自动运行,需要通过事件循环(如
asyncio
)来驱动。
- 是一个暂停和恢复执行的函数,通过
-
事件循环(Event Loop)
- 是协程调度的核心,负责管理任务的执行顺序、IO事件的监听和处理。
- Python标准库
asyncio
提供了事件循环的实现。
二、协程的常见用法
1. 定义和运行协程
import asyncioasync def my_coroutine():print("Coroutine started")await asyncio.sleep(1) # 模拟耗时操作(如IO)print("Coroutine finished")# 运行协程
asyncio.run(my_coroutine()) # Python 3.7+ 推荐
2. 使用 async
和 await
async def
:定义一个协程函数。await
:在协程内部调用另一个协程,当遇到await
时,当前协程暂停,让出控制权。
async def fetch_data():print("Start fetching")await asyncio.sleep(2) # 模拟网络请求return "Data"async def main():result = await fetch_data() # 等待fetch_data完成print(result) # 输出:Dataasyncio.run(main())
3. 并发执行多个协程
使用 asyncio.gather()
并发运行多个协程:
async def task1():await asyncio.sleep(1)return "Task1 Done"async def task2():await asyncio.sleep(2)return "Task2 Done"async def main():results = await asyncio.gather(task1(), task2())print(results) # 输出:["Task1 Done", "Task2 Done"]asyncio.run(main())
4. 异步迭代和上下文管理器
- 异步生成器:通过
async for
迭代异步序列。 - 异步上下文管理器:通过
async with
管理资源。
# 异步生成器示例
async def async_gen():for i in range(3):await asyncio.sleep(1)yield iasync def main():async for item in async_gen():print(item) # 输出0, 1, 2# 异步上下文管理器示例(如打开文件)
async with aiofiles.open("file.txt", mode="r") as f:content = await f.read()
三、常用协程类库
以下是Python中常用的协程相关库及典型用法:
1. asyncio
(标准库)
Python内置的异步事件驱动框架,提供协程、事件循环、Future/Task等核心功能。
- 核心组件:
- Event Loop:事件循环管理器(如
asyncio.get_event_loop()
)。 - Task:将协程封装为任务,以便在事件循环中调度。
- Future:表示异步操作的最终结果。
- Event Loop:事件循环管理器(如
- 典型用法:
async def hello():print("Hello")await asyncio.sleep(1)print("World")# 获取事件循环并运行 loop = asyncio.get_event_loop() loop.run_until_complete(hello()) # 或 asyncio.run(hello())
2. aiohttp
基于 asyncio
的异步HTTP客户端和服务器库,适用于高性能Web爬虫或Web服务。
- 客户端用法:
import aiohttpasync def fetch():async with aiohttp.ClientSession() as session:async with session.get("https://api.example.com/data") as response:return await response.json()asyncio.run(fetch())
- 服务器用法:
from aiohttp import webasync def handle(request):return web.Response(text="Hello, Aiohttp!")app = web.Application() app.router.add_get("/", handle) web.run_app(app)
3. asyncpg
用于 PostgreSQL 的异步数据库驱动,适用于异步数据库操作。
import asyncpgasync def main():conn = await asyncpg.connect(user='user', password='password',database='db', host='127.0.0.1')values = await conn.fetch("SELECT * FROM my_table")await conn.close()asyncio.run(main())
4. aiofiles
异步文件操作库,替代 open()
函数,适用于大文件处理或需要异步读写的场景。
import aiofilesasync def read_file():async with aiofiles.open("large_file.txt", mode="r") as f:content = await f.read()print(content)
5. gevent
基于 greenlet
的协程库,通过协程模拟多线程,支持同步代码异步化(非async/await语法)。
import gevent
from gevent import monkey; monkey.patch_all() # 打补丁def task(name, n):for i in range(n):print(f"{name}: {i}")gevent.sleep(0.1)gevent.joinall([gevent.spawn(task, "A", 3),gevent.spawn(task, "B", 5)
])
四、协程 vs 线程/进程
特性 | 协程 | 多线程 | 多进程 |
---|---|---|---|
资源消耗 | 极低(共享线程/进程资源) | 中(线程资源) | 高(进程资源) |
切换方式 | 用户态协作式切换 | 内核级抢占式切换 | 内核级抢占式切换 |
适合场景 | IO密集型(如网络请求、文件) | 轻量级并发(如小计算任务) | CPU密集型(如科学计算) |
GIL影响 | 在CPython中受GIL限制 | 受GIL限制 | 不受GIL限制(每个进程独立) |
五、最佳实践与注意事项
- 避免阻塞操作:协程内部应避免长时间阻塞(如
time.sleep()
),改用asyncio.sleep()
。 - 合理使用
await
:确保在协程中正确使用await
,否则代码不会异步执行。 - 错误处理:使用
try/except
捕获异步操作的异常。 - 调试:协程的调试较复杂,建议使用
asyncio.debug
或专用调试工具。 - 库的兼容性:非异步库需要通过
loop.run_in_executor()
转换为异步操作。
六、典型应用场景
- Web爬虫:并发请求多个网页,异步处理响应。
- 实时数据处理:如股票行情、物联网传感器数据流。
- 高性能服务器:构建异步HTTP服务器或WebSocket服务。
- 游戏或模拟器:需要处理大量并发事件的场景。
七、扩展学习资源
- 官方文档:
asyncio
:https://docs.python.org/3/library/asyncio.htmlaiohttp
:https://aiohttp.readthedocs.io/
- 书籍:
- 《Fluent Python》第22章(协程和事件循环)。
- 《Python异步编程实战》(异步IO、协程及框架应用)。
通过合理使用协程和相关库,可以显著提升Python在IO密集型任务中的性能和响应能力!