Python协程深度解析
Python中的协程(Coroutine) 是一种轻量级的异步执行单元,主要用于解决IO密集型任务的性能问题。Python 3.5引入了 async/await
语法,使得协程变得简洁且易于使用。协程的核心是通过事件循环(Event Loop) 来调度任务,在等待外部操作(如网络请求、文件读写)时,自动切换到其他任务,从而提升程序的整体效率。
一、协程的基本概念
-
协程(Coroutine)
- 是一个暂停和恢复执行的函数,通过
async def
定义。 - 协程不会阻塞整个事件循环,而是通过
await
关键字主动让出控制权。 - 协程本身不会自动运行,需要通过事件循环(如
asyncio
)来驱动。 -
事件循环(Event Loop)
- 是协程调度的核心,负责管理任务的执行顺序、IO事件的监听和处理。
- Python标准库
asyncio
提供了事件循环的实现。
二、协程的常见用法
1. 定义和运行协程
import asyncio
async 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) # 输出:Data
asyncio.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 i
async 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等核心功能。
asyncio.get_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 aiohttp
async 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 web
async 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 asyncpg
async 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 aiofiles
async 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密集型任务中的性能和响应能力!
作者:高效匠人