Python 进程与进程池
本章节主要讲解 进程是什么? 操作系统基础知识,进程与程序的区别,进程特性,创建单个进程,运行多个进程,进程池(Pool),基本使用:map方法,进程池异步执行,进程与进程池的选择,注意事项与最佳实践。
1. 进程是什么?
想象你的电脑是个超级忙碌的餐厅,CPU 是大厨,内存是食材库,进程就是一道道正在烹饪的菜。每个进程都有自己的“灶台”和“食材”(独立的内存空间),互不干扰,由操作系统这位“餐厅经理”来调度,谁先上菜、谁多占点火力,全靠它指挥。
操作系统基础知识
- 进程:操作系统分配资源的基本单位,像是餐厅里每张桌子上的订单,包含程序代码、数据和执行状态。
- 多核 CPU:现代电脑有多个“厨师”(核心),多进程可以让每个核心同时干活,效率翻倍!
- 进程隔离:每个进程的内存是独立的,进程 A 不会偷看进程 B 的“秘方”,安全又省心。
- 调度:操作系统像个时间管理大师,用算法(比如时间片轮转)决定哪个进程先跑、跑多久。
- 线程 vs 进程:线程是进程里的“小帮工”,共享内存,适合 I/O 密集任务;多进程则适合 CPU 密集任务,比如计算密集的数学运算。
Python 的 multiprocessing
模块就是你的“超级助手”,帮你轻松管理多进程,绕过 Python 的全局解释器锁(GIL),让多核 CPU 火力全开!
进程与程序的区别
进程是程序执行时的动态实例,包含独立的内存空间和系统资源,通过系统命令查看进程,在Linux/macOS终端执行: ps aux | grep python
,在Windows CMD执行: tasklist | findstr python
进程特性
- 独立性:拥有独立地址空间
- 动态性:生命周期状态(创建/就绪/运行/阻塞/终止)
- 并发性:现代操作系统通过时间片轮转实现多进程"同时"运行
2. 创建单个进程
multiprocessing.Process
类用于创建单个进程。以下是一个基本示例,展示如何启动一个进程并执行任务。
import multiprocessing
import time
def worker(name):
print(f"进程 {name} 开始执行")
time.sleep(2)
print(f"进程 {name} 执行结束")
if __name__ == '__main__':
process = multiprocessing.Process(target=worker, args=('Worker1',))
process.start()
process.join()
print("主进程结束")
target
指定进程执行的函数,args
传递参数。start()
启动进程,join()
等待进程完成。if __name__ == '__main__':
防止在Windows上重复导入模块。
应用场景:适合简单的并行任务,如单个独立计算或I/O操作。
3. 运行多个进程
当需要同时运行多个任务时,可以创建多个进程。以下示例展示如何并行运行多个进程。
import multiprocessing
import time
def worker(name):
print(f"进程 {name} 开始执行")
time.sleep(2)
print(f"进程 {name} 执行结束")
if __name__ == '__main__':
processes = []
for i in range(3):
p = multiprocessing.Process(target=worker, args=(f"Worker{i+1}",))
processes.append(p)
p.start()
for p in processes:
p.join()
print("所有进程执行完毕")
- 创建多个
Process
对象,存入列表。 - 依次启动并等待所有进程完成。
应用场景:适合需要并行执行多个独立任务的场景,如批量数据处理或并行下载。
4. 进程池(Pool)
multiprocessing.Pool
用于管理一组进程,自动分配任务到可用进程,适合处理大量任务。进程池通过限制同时运行的进程数量,优化资源使用。
基本使用:map方法
Pool.map()
方法将任务分发到进程池,类似于Python内置的map
函数。
import multiprocessing
def square(num):
return num * num
if __name__ == '__main__':
with multiprocessing.Pool(processes=4) as pool:
numbers = [1, 2, 3, 4, 5]
results = pool.map(square, numbers)
print(f"结果: {results}")
Pool(processes=4)
创建包含4个进程的进程池。map
将square
函数应用于numbers
中的每个元素。with
语句确保进程池正确关闭。map
会阻塞主进程,直到所有任务完成并返回结果
应用场景:适合需要对数据列表进行相同操作的场景,如批量计算或数据转换。
5. 进程池异步执行
apply_async
允许异步提交任务,返回结果对象,可以稍后获取结果。
import multiprocessing
import time
def process_task(data):
time.sleep(1)
return f"处理 {data} 完成"
if __name__ == '__main__':
with multiprocessing.Pool(processes=3) as pool:
tasks = [pool.apply_async(process_task, args=(i,)) for i in range(5)]
results = [task.get() for task in tasks]
print(f"结果: {results}")
代码说明:
apply_async
异步提交任务,返回ApplyResult
对象,不会阻塞主进程,提交后边返回。get()
获取任务结果,阻塞直到任务完成。
应用场景:适合需要动态提交任务或处理结果的场景,如实时任务调度。
6. 进程与进程池的选择
- 使用单个进程:
- 任务数量少,逻辑简单。
- 需要精细控制进程行为。
- 示例:单次大型计算任务。
- 使用进程池:
- 任务数量多,适合批量处理。
- 需要自动管理进程分配。
- 示例:大规模数据处理、并行计算。
7. 注意事项与最佳实践
- 平台兼容性:在Windows上,必须将进程相关代码放在
if __name__ == '__main__':
中。 - 资源管理:进程池使用
with
语句确保资源正确释放。 - 任务粒度:任务应足够大以抵消进程创建开销,避免过于琐碎的任务。
- 错误处理:异步任务需检查异常,使用
try-except
捕获get()
可能的错误。
import multiprocessing
def risky_task(num):
if num == 3:
raise ValueError("错误示例")
return num * 2
if __name__ == '__main__':
with multiprocessing.Pool(processes=2) as pool:
tasks = [pool.apply_async(risky_task, args=(i,)) for i in range(5)]
for task in tasks:
try:
result = task.get()
print(f"结果: {result}")
except Exception as e:
print(f"捕获错误: {e}")