在Python并发编程的演进中,进程、线程、生成器、协程是绕不开的核心概念。进程是资源隔离的最小单位,线程是CPU调度的基本单元,而生成器是协程的基础,协程则是轻量级的用户态并发方案。本文将通过5个实战代码文件,从生成器推导式到协程实战,逐步拆解核心逻辑,并对比进程、线程的差异,帮你彻底掌握高效并发编程。
一、核心概念前置:进程 vs 线程 vs 协程
先理清三者的本质区别,这是理解后续代码的基础:
| 特性 | 进程 | 线程 | 协程 |
|---|---|---|---|
| 资源占用 | 高,独立内存空间 | 中,共享进程内存 | 极低,共享线程栈 |
| 切换开销 | 极大(操作系统调度,内核态) | 较大(操作系统调度,内核态) | 极小(用户态自行切换) |
| 并发能力 | 多核并行,适合CPU密集型 | 受GIL限制,Python中无真正并行 | 单线程并发,适合IO密集型 |
| 实现难度 | 中 | 中 | 低(基于生成器/asyncio) |
| 核心依赖 | os.fork/multiprocessing | threading | 生成器/yield/asyncio |
核心结论 :Python中线程受GIL(全局解释器锁)限制,无法实现多核并行;协程不依赖操作系统调度 ,在单线程内完成任务切换,是IO密集型场景(爬虫、文件读写、网络请求)的最优解。而生成器是协程的底层基石。
二、实战代码:从生成器到协程
01_回顾推导式.py
推导式是Python的语法糖,支持快速创建列表、字典、集合,也是理解生成器推导式的基础。
python
# 1. 列表推导式:立即创建所有元素,占用内存大
list_comp = [i for i in range(5)]
print("列表推导式:", list_comp) # 输出:[0,1,2,3,4]
# 2. 字典推导式
dict_comp = {i: i*2 for i in range(3)}
print("字典推导式:", dict_comp) # 输出:{0:0,1:2,2:4}
# 3. 集合推导式
set_comp = {i for i in range(3)}
print("集合推导式:", set_comp) # 输出:{0,1,2}
核心特点 :推导式会立即生成所有数据,适合数据量小的场景;数据量过大时,会占用大量内存。
02_生成器推导式.py
生成器推导式是惰性求值 的迭代器,语法和列表推导式一致,仅将[]改为(),不立即生成数据,节省内存。
python
# 生成器推导式:惰性生成元素,内存占用极低
gen = (i for i in range(5))
print("生成器对象:", gen) # 输出:<generator object <genexpr> at 0xxxx>
# 遍历生成器:每次调用next()才生成一个元素
print(next(gen)) # 0
print(next(gen)) # 1
# 循环遍历:自动调用next(),直到无元素
for num in gen:
print("循环遍历:", num) # 2,3,4
核心特点:
- 惰性求值:用到才生成,不占用额外内存;
- 只能遍历一次,遍历完后自动销毁;
- 是Python中轻量级的迭代器实现。
03_yield生成器.py
yield是生成器的核心关键字,替代return,让函数变成生成器函数,支持暂停/恢复执行。
python
# 定义生成器函数:包含yield关键字
def generator_func():
yield 1 # 暂停,返回1
yield 2 # 暂停,返回2
yield 3 # 暂停,返回3
# 创建生成器对象
gen = generator_func()
print("生成器函数对象:", gen) # <generator object generator_func at 0xxxx>
# 手动获取元素
print(next(gen)) # 1
print(next(gen)) # 2
# 循环获取剩余元素
for num in gen:
print("剩余元素:", num) # 3
核心原理:
- 调用
next()时,函数执行到yield暂停,返回当前值; - 再次调用
next(),从暂停位置继续执行,直到下一个yield; - 无
yield可执行时,抛出StopIteration异常。
进阶:带参数的生成器(send方法)
生成器不仅能产出值,还能接收外部传入的值,这是协程的基础:
python
def param_gen():
while True:
# 接收外部send的值,赋值给x
x = yield
print(f"接收到的值:{x}")
gen = param_gen()
next(gen) # 预激活生成器
gen.send(10) # 发送10,输出:接收到的值:10
gen.send(20) # 发送20,输出:接收到的值:20
04_协程的应用.py
协程是用户态的轻量级线程 ,基于生成器的yield暂停/恢复特性实现,无需操作系统调度,单线程内实现并发。
Python3.5+推荐使用async/await语法(底层仍依赖生成器),实战如下:
python
import asyncio
import time
# 定义协程函数:async关键字修饰
async def task(name, delay):
print(f"协程任务{name}开始,耗时{delay}秒")
# await:暂停当前协程,执行其他协程,实现非阻塞等待
await asyncio.sleep(delay)
print(f"协程任务{name}完成")
# 主协程:调度多个协程
async def main():
# 创建协程任务列表
tasks = [
asyncio.create_task(task("A", 2)),
asyncio.create_task(task("B", 1)),
]
# 并发执行所有任务
await asyncio.gather(*tasks)
if __name__ == "__main__":
start = time.time()
# 运行协程
asyncio.run(main())
end = time.time()
print(f"总耗时:{end - start:.2f}秒")
运行结果:
协程任务A开始,耗时2秒
协程任务B开始,耗时1秒
协程任务B完成
协程任务A完成
总耗时:2.01秒
核心逻辑:
async定义协程函数,不能直接执行,需放入事件循环;await:遇到IO等待时,自动切换到其他协程,不阻塞线程;- 两个任务总耗时≈最长任务耗时,实现真正的并发。
05_示例体验.py
综合对比:同步执行 vs 协程并发,直观感受协程的效率提升。
python
import asyncio
import time
# 同步函数:阻塞执行
def sync_task(name, delay):
print(f"同步任务{name}开始,耗时{delay}秒")
time.sleep(delay)
print(f"同步任务{name}完成")
# 协程函数:非阻塞并发
async def async_task(name, delay):
print(f"协程任务{name}开始,耗时{delay}秒")
await asyncio.sleep(delay)
print(f"协程任务{name}完成")
# 1. 同步执行测试
print("===== 同步执行 =====")
start = time.time()
sync_task("A", 2)
sync_task("B", 1)
print(f"同步总耗时:{time.time() - start:.2f}秒\n")
# 2. 协程并发测试
print("===== 协程并发 =====")
async def main():
await asyncio.gather(
async_task("A", 2),
async_task("B", 1),
)
start = time.time()
asyncio.run(main())
print(f"协程总耗时:{time.time() - start:.2f}秒")
运行结果:
===== 同步执行 =====
同步任务A开始,耗时2秒
同步任务A完成
同步任务B开始,耗时1秒
同步任务B完成
同步总耗时:3.01秒
===== 协程并发 =====
协程任务A开始,耗时2秒
协程任务B开始,耗时1秒
协程任务B完成
协程任务A完成
协程总耗时:2.01秒
核心结论:同步执行总耗时=所有任务耗时之和,协程并发总耗时≈最长任务耗时,效率提升显著。
三、生成器与协程的核心关系
- 生成器是协程的底层基础 :协程的暂停/恢复,依赖生成器
yield的特性; - 生成器侧重数据生成:逐个产出数据,节省内存;
- 协程侧重任务并发:在单线程内切换IO任务,提升并发效率。
四、适用场景总结
- 进程:CPU密集型任务(大数据计算、视频编码),利用多核;
- 线程:IO密集型+低并发场景,受GIL限制,效率一般;
- 生成器:大数据量迭代(读取大文件、海量数据生成);
- 协程:高并发IO密集型场景(爬虫、API接口、WebSocket、文件批量读写)。
总结
本文通过5个实战代码,从基础推导式过渡到生成器,再基于生成器实现协程,同时对比了进程、线程、协程的核心差异:
- 推导式是语法糖,立即生成数据;生成器推导式惰性求值,节省内存;
yield是生成器核心,支持暂停/恢复,是协程的底层依赖;- 协程基于用户态切换,无操作系统开销,是Python高并发IO的最优解;
- 进程/线程适合不同场景,协程则是轻量级并发的终极选择。
掌握生成器和协程,能让你的Python代码在IO密集型场景下,用极低的资源实现超高并发,这也是Python进阶编程的必备技能。