1. 什么是协程?
协程(Coroutine)是一种轻量级的线程,由用户控制调度,可以在执行过程中暂停和恢复。与线程不同,协程的切换由程序显式控制,而不是由操作系统调度。
核心特点
- 协作式多任务:协程主动让出控制权,而不是被强制切换。
- 单线程内并发:多个协程可以在一个线程内交替执行。
- 高效:协程的切换开销远小于线程。
2. 协程的实现方式
在 Python 中,协程主要通过以下方式实现:
-
生成器(Generator): 使用 yield 关键字实现协程。
-
asyncio 模块: Python 3.4 引入的标准库,提供对协程的原生支持。
-
async/await 语法: Python 3.5 引入,简化协程的编写。
3. 协程的优势
3.1 高效并发
-
轻量级:协程的创建和切换开销远小于线程。
-
高并发:单线程内可运行成千上万个协程。
3.2 避免 GIL 限制
- Python 的 GIL(全局解释器锁)限制了多线程的并行能力,而协程在单线程内运行,不受 GIL 影响。
3.3 简化异步编程
- 通过 async/await 语法,可以像编写同步代码一样编写异步代码,避免回调地狱(Callback Hell)。
3.4 资源利用率高
- 协程在等待 I/O 操作时主动让出控制权,避免线程阻塞,提高 CPU 和内存的利用率。
4. 协程的应用场景
4.1 I/O 密集型任务
-
网络请求:如 HTTP 请求、数据库查询。
-
文件读写:如异步读取大文件。
4.2 高并发服务
-
Web 服务器:如 FastAPI、Sanic。
-
实时通信:如 WebSocket、消息队列。
4.3 任务调度
-
定时任务:如周期性执行任务。
-
事件驱动:如 GUI 事件处理。
5. 协程示例
5.1 使用 asyncio 实现协程
import asyncio
async def task(name, delay):
print(f"{name} 开始")
await asyncio.sleep(delay) # 模拟 I/O 操作
print(f"{name} 完成")
async def main():
# 创建多个协程任务
tasks = [
task("任务-1", 2),
task("任务-2", 1),
task("任务-3", 3),
]
await asyncio.gather(*tasks) # 并发执行
# 运行协程
asyncio.run(main())
输出:
任务-1 开始
任务-2 开始
任务-3 开始
任务-2 完成
任务-1 完成
任务-3 完成
5.2 使用 async/await 实现 HTTP 请求
import aiohttp
import asyncio
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 = [
"https://www.example.com",
"https://www.python.org",
"https://www.github.com",
]
tasks = [fetch(url) for url in urls]
results = await asyncio.gather(*tasks)
for url, content in zip(urls, results):
print(f"{url} 的内容长度: {len(content)}")
# 运行协程
asyncio.run(main())
6. 协程与多线程、多进程的对比
特性 | 协程 | 多线程 | 多进程 |
---|---|---|---|
并发方式 | 单线程内协作式并发 | 多线程并行 | 多进程并行 |
资源开销 | 极小 | 较大 | 最大 |
GIL 影响 | 不受 GIL 限制 | 受 GIL 限制 | 不受 GIL 限制 |
适用场景 | I/O 密集型任务 | I/O 密集型任务 | CPU 密集型任务 |
实现复杂度 | 较低(async/await 语法) | 中等(需处理线程同步问题) | 较高(需处理进程间通信) |
7. 总结
- 协程是一种轻量级的并发编程方式,适合 I/O 密集型任务和高并发场景。
- 通过 asyncio 和 async/await,可以简化异步编程,提高代码可读性和性能。
- 与多线程和多进程相比,协程的资源开销更小,并发能力更强。