【Python】进程、线程、协程、生成器
1. 进程、线程、协程对比
| 类型 | 开销 | 并发方式 | 适用场景 | GIL |
|---|---|---|---|---|
| 协程 | 极小 | 单线程内切换 | 超高 IO 并发、爬虫、接口 | 不影响 |
| 线程 | 小 | 线程切换 | 普通 IO 并发 | 受 GIL |
| 进程 | 大 | 多核并行 | CPU 密集计算 | 不受 GIL |
2. Python 进程
(1)进程的特点
内存完全隔离
不受GIL锁限制:
- GIL锁:本质上是个互斥锁。它确保同一时刻只有一个线程执行Python字节码,是CPython解释器为保证线程安全而设计的互斥机制。
开销比创建线程和协程大。
独立的PID
(2)进程的使用场景
【推荐场景】
CPU 密集型计算(最常用)
- 大规模数值运算、矩阵运算
- 数据分析、数据清洗大批量数据
- 加密解密、哈希爆破、暴力运算
- 深度学习数据预处理、批量特征计算
大批量并发独立任务
- 批量爬取大量站点(高并发爬取)
- 批量压缩 / 解压文件
- 批量图片处理:裁剪、水印、转格式
- 批量音视频转码、剪辑
任务隔离,崩溃互补影响
- 子任务容易报错崩溃,不想主程序挂掉
- 独立业务模块拆分,一个进程崩不影响整体
- 沙箱执行用户代码、执行未知脚本
突破单进程内存 / 资源限制
- 单个任务占用内存极大,拆分多进程分担内存
- 限制单进程 CPU 使用率,拆分负载
【不推荐场景】
- 需要频繁共享大量数据。
- 任务执行速度极快,频次极高:进程的创建和销毁远高于线程和携程。
- IO密集型(网络请求,文件读写,接口请求)
(3)代码示例
- 简单创建
import multiprocessing
import time
def run():
print("我是子进程")
time.sleep(2)
if __name__ == '__main__':
# 创建子进程
p = multiprocessing.Process(target=run)
p.start() # 启动
p.join() # 等待结束
-
进程池
- map批量统一执行
import time from multiprocessing import Pool def work(x): time.sleep(1) return x * 2 if __name__ == '__main__': # 开启4个进程 with Pool(4) as p: # 自动分配任务 res = p.map(work, [1,2,3,4,5,6]) print(res)- apply_async 异步提交(最常用):不阻塞,还可以拿结果
import time from multiprocessing import Pool def task(num): time.sleep(1) print(f"执行任务{num}") return num if __name__ == '__main__': pool = Pool(3) # 最大3个进程 result_list = [] for i in range(6): # 提交任务 ret = pool.apply_async(task, args=(i,)) result_list.append(ret) # 关闭进程池,不再接收新任务 pool.close() # 等待所有任务完成 pool.join() # 取出所有结果 for r in result_list: print(r.get())- 回调函数
from multiprocessing import Pool def func(n): return n**2 def callback(res): print("结果:",res) if __name__ == '__main__': with Pool(2) as p: p.apply_async(func,args=(5,),callback=callback) p.join()- 进程池 + 队列
from multiprocessing import Pool,Queue def worker(q): while not q.empty(): data = q.get() print("处理:",data) if __name__ == '__main__': q = Queue() # 存入任务 for i in range(10): q.put(i) pool = Pool(3) for _ in range(3): pool.apply_async(worker,args=(q,)) pool.close() pool.join()
3. Python 线程
(1)线程的特点
- 资源共享
- 开销极小
- 受GIL全局解释器锁限制
(2)线程的场景
【推荐场景】
IO 密集型任务(最核心)
- 网络请求:爬虫、接口调用、HTTP 请求
- 文件操作:批量读写本地文件
- 数据库查询、Redis 读写
- 串口、网络连接等待
- 【特点】:大部分时间在等待阻塞,CPU 空闲,线程切换开销极小
大量轻量并发任务
- 批量发送消息、短信、邮件
- 批量访问接口、批量校验数据
- 定时批量小任务
需要共享大量数据
- 线程共享同一块内存空间
- 全局变量、列表字典直接共用
- 通信简单、不用管道 / 队列,开发简单
任务执行快、启停频繁
- 线程创建销毁开销远小于进程
- 短时小任务优先线程
界面程序后台常驻任务
- GUI 软件后台刷新、监听、轮询
- 不卡死主界面
【不推荐场景】
- CPU 密集型计算:大数据运算、循环暴力计算、矩阵运算、加密解密;GIL 限制同一时刻只有一个线程跑,多线程反而变慢
- 需要利用多核 CPU 跑满性能:线程做不到。
(3)代码示例
- 简单创建
import threading
import time
def job():
print("子线程开始运行")
time.sleep(2)
print("子线程结束")
if __name__ == "__main__":
# 创建线程
t = threading.Thread(target=job)
# 启动
t.start()
# 等待线程结束
t.join()
print("主线程走完")
-
线程池
- 基础用法
import time from concurrent.futures import ThreadPoolExecutor def task(n): time.sleep(1) print(f"执行任务{n}") return n*2 if __name__ == '__main__': # 最大并发5个线程 with ThreadPoolExecutor(max_workers=5) as t: for i in range(10): t.submit(task, i)- 获取任务返回结果
from concurrent.futures import ThreadPoolExecutor def func(x): return x+10 if __name__ == '__main__': with ThreadPoolExecutor(3) as pool: res = [pool.submit(func,i) for i in range(5)] for r in res: print(r.result()) # 拿返回值- map批量传参
from concurrent.futures import ThreadPoolExecutor def work(x): return x**2 if __name__ == '__main__': with ThreadPoolExecutor(4) as t: ret = t.map(work, [1,2,3,4,5]) print(list(ret))- 线程池 + 队列
from concurrent.futures import ThreadPoolExecutor from queue import Queue import time q = Queue() # 放入任务 for i in range(20): q.put(i) def handle(): while not q.empty(): num = q.get() time.sleep(0.5) print(f"处理:{num}") q.task_done() if __name__ == '__main__': with ThreadPoolExecutor(5) as pool: for _ in range(5): pool.submit(handle)
4. 协程
(1)什么是协程
微线程,用户态轻量级并发,单线程内切换执行
- 不用开线程、不开进程,一条主线程里来回切换任务
- 开销极小,几万、几十万协程轻松跑
- 自带自动切换:遇到 IO 阻塞就切走,不卡 CPU
(2)核心特点
- 极致轻量:创建成本远低于线程
- 单线程并发:不占用多核心,只利用等待时间
- 无锁竞争:全程单线程,不用互斥锁
- 只适合 IO 阻塞:网络请求、爬虫、读写、延时
- CPU 密集无效:纯计算用协程反而变慢
(3)协程的场景
【推荐场景】
超高并发 IO 密集(最主流)
- 海量爬虫、批量请求接口
- 大量 HTTP/HTTPS 异步请求
- 批量调用第三方 API、微服务接口
- 异步数据库、Redis、MQ 读写
大量延时等待任务
- 批量定时任务、延时执行
- 轮询监听、长连接心跳
- 大量休眠等待类业务
高并发服务端开发
- 异步 Web 框架:FastAPI、Sanic、Tornado
- 搭建高吞吐接口服务、网关服务
- 后端推送、消息推送服务
轻量海量并发任务
- 上万级别的轻量 IO 任务,线程池会卡线程上限,协程轻松顶住
异步文件 / 网络读写
- 异步读写文件、异步 TCP/UDP 通信
【不推荐场景】
- CPU 密集计算:协程单线程串行执行,计算任务会直接卡死整个事件循环,越跑越慢
- 同步阻塞代码混用:大量time.sleep()、同步请求、同步数据库、会阻塞整个协程池
- 需要利用多核并行计算:协程天生单线程,要多核必须协程 + 多进程组合
(4)代码示例
- 基础用法
import asyncio
# 定义协程函数
async def say_hello(name):
print(f"开始 {name}")
# 模拟IO等待,自动切换任务
await asyncio.sleep(2)
print(f"结束 {name}")
async def main():
# 并发运行多个协程
task1 = asyncio.create_task(say_hello("任务1"))
task2 = asyncio.create_task(say_hello("任务2"))
await task1
await task2
# 运行协程程序
asyncio.run(main())
-
并发使用
- gather批量并发(最常用)
import asyncio async def work(num): await asyncio.sleep(1) print(f"完成任务{num}") async def main(): tasks = [work(i) for i in range(5)] await asyncio.gather(*tasks) asyncio.run(main())- 顺序执行(不用协程优势)
async def main(): await work(1) await work(2)- 异步请求实战(爬虫标配)
import aiohttp import asyncio async def get_url(url): async with aiohttp.ClientSession() as session: async with session.get(url) as res: print(await res.text()) async def main(): urls = ["https://www.***.com"]*3 tasks = [get_url(u) for u in urls] await asyncio.gather(*tasks) asyncio.run(main())
5. 生成器
(1)什么是生成器?
一边循环,一边生成数据,不一次性存全部数据。
(2)生成器的特点
- 惰性执行:(懒加载):不会一次性生成所有数据,用一个生成一个,不用不生成
- 极度省内存:不存放全部元素,值存生成规则,海量数据也不爆内存。
- 单向不可逆:只能从前向后遍历,遍历完就空了,不能回头重读
- 暂停 / 恢复执行:yield 遇到就暂停,下次迭代从暂停位置继续执行。
- 无法索引 / 切片:不能用 gen[0]取值,没有下标,不是列表。
- 一次性消费:遍历一次结束,再次遍历拿不到任何数据。
(3)生成器的场景
【推荐场景】
- 读取超大文件
- 遍历海量数据 / 数据库分页查询
- 生成无限序列
- 爬虫分批请求数据
- 数据过滤,清晰,流式处理。
- 协程 / 任务调度(yield实现简单协程)
- 迭代器嵌套,多层数据拆解。
- 大数据运算,数据推演
【不推荐场景】
- 需要重复多次遍历同一组数据
- 需要随机访问数据(下标取值)
(4)代码示例:
- 基础示例
# 定义生成器函数
def my_generator():
yield 1 # 第一次调用返回1,暂停在这里
yield 2 # 第二次调用返回2,暂停在这里
yield 3 # 第三次调用返回3
# 创建生成器对象
gen = my_generator()
print(gen) # <generator object my_generator at 0x...>
# 取值
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
- 进阶示例
# 生成 1~n 的平方数
def square_gen(n):
for i in range(1, n+1):
yield i ** 2 # 每次循环暂停,返回平方值
# 使用
gen = square_gen(3)
for num in gen:
print(num) # 依次输出 1,4,9

浙公网安备 33010602011771号