Python 协程
1. 协程的概念
协程是一种 用户态的轻量级线程,它与线程类似可以执行任务,但更轻量、不依赖操作系统线程。Python 协程通过 async
/await
关键字实现。
特点:
- 非阻塞:协程可以在等待 I/O 时暂停自己,让出执行权,其他协程可以继续执行。
- 单线程内实现高并发:不像多线程多进程,需要线程切换开销,协程切换开销极小。
- 协作式调度:协程主动“让出”执行权,而不是操作系统强制切换。
通俗理解:
- 线程是“抢资源做事”,操作系统说谁先运行就先运行。
- 协程是“按顺序做事”,任务自己说什么时候暂停,什么时候继续。
2. Python 协程基础
2.1 定义协程
Python 3.5+ 使用 async def
定义协程函数:
python
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1) # 模拟耗时操作
print("World")
这里的 await
表示挂起协程,等待某个异步操作完成。
2.2 调用协程
协程函数本身不会立即执行,需要通过事件循环运行:
python
asyncio.run(say_hello())
事件循环(Event Loop)是协程运行的核心,它负责调度协程的执行和挂起。
3. 协程 vs 线程 vs 同步 I/O
特性 | 同步 | 线程 | 协程 |
---|---|---|---|
并发方式 | 串行 | 并行/并发 | 并发 |
I/O阻塞 | 会阻塞 | 不阻塞 | 不阻塞 |
开销 | 小 | 大 | 很小 |
切换方式 | N/A | 系统调度 | 协作调度 |
例子对比(异步下载多个网页):
- 同步:
python
def download(url):
# 阻塞 I/O
pass
for url in urls:
download(url)
- 协程:
python
async def download(url):
await aiohttp.get(url) # 非阻塞
协程可以在等待网络请求时执行其他任务,提高效率。
4. asyncio 库核心概念
- 协程函数:用
async def
定义。 - Task:任务对象,包装协程,交给事件循环执行。
- 事件循环:调度所有协程任务。
- Future:表示异步操作的最终结果。
示例:
python
import asyncio
async def task(name, delay):
print(f"{name} 开始")
await asyncio.sleep(delay)
print(f"{name} 结束")
return f"{name} result"
async def main():
t1 = asyncio.create_task(task("A", 2))
t2 = asyncio.create_task(task("B", 1))
results = await asyncio.gather(t1, t2)
print(results)
asyncio.run(main())
输出顺序:
A 开始
B 开始
B 结束
A 结束
['A result', 'B result']
解释:
- 协程 A 和 B 并发执行。
- B 耗时更短,所以先结束。
5. 使用协程的场景
- 高并发 I/O 密集型任务:Web 服务、爬虫、网络请求。
- 异步操作:数据库查询、文件读写、网络通信。
- GUI、游戏循环:避免阻塞界面。
注意:
- 协程不适合 CPU 密集型任务,计算密集型任务仍需多线程或多进程。
6. 小结
- 协程 = 用户态轻量线程,靠
await
挂起和恢复。 - Python 协程基于
asyncio
,核心是事件循环。 - 协程非常适合 高并发 I/O 场景。
- 学好协程,可以写出既高效又轻量的网络程序。