在Python中,多进程是实现并行计算和充分利用多核CPU的重要技术。与多线程不同,多进程可以绕过GIL(全局解释器锁)的限制,真正实现并行执行。本教程将详细讲解Python中创建进程的两种主要方法,并提供实际代码示例。
为什么使用多进程?
Python的GIL限制了同一时刻只能有一个线程执行Python字节码,这使得多线程在CPU密集型任务中无法充分利用多核处理器。多进程通过创建独立的Python解释器进程,每个进程有自己的GIL,从而实现了真正的并行执行。
CPU密集型任务
对于需要大量计算的任务(如数学计算、图像处理),多进程可以显著提高执行速度
隔离性
进程间内存空间相互隔离,一个进程崩溃不会影响其他进程
充分利用多核
每个进程可以在不同的CPU核心上运行,充分利用现代多核处理器
方法一:使用函数创建进程
这是最直接的方法,通过创建multiprocessing.Process
实例并传入目标函数来创建新进程。
步骤:
- 导入
multiprocessing
模块 - 定义目标函数
- 创建
Process
对象,指定目标函数和参数 - 启动进程
- 等待进程结束(可选)
import multiprocessing
import os
import time
# 定义目标函数
def worker_process(task_id, delay):
print(f"进程 {task_id} (PID: {os.getpid()}) 开始执行,延迟 {delay}秒")
time.sleep(delay)
print(f"进程 {task_id} 完成")
if __name__ == '__main__':
# 创建进程列表
processes = []
# 创建并启动三个进程
for i in range(3):
p = multiprocessing.Process(
target=worker_process, # 目标函数
args=(i, i+1) # 传递给函数的参数
)
processes.append(p)
p.start()
print(f"主进程: 启动了进程 {i}")
# 等待所有进程完成
for p in processes:
p.join()
print("所有进程执行完毕!")
代码解析:
multiprocessing.Process
:进程类,用于创建新进程target
:指定要在新进程中运行的函数args
:传递给目标函数的参数(以元组形式)start()
:启动进程join()
:阻塞当前进程,直到目标进程完成if __name__ == '__main__'
:在Windows系统中必须使用,避免子进程无限递归
方法二:通过继承Process类创建进程
这种方法通过创建Process
的子类并重写run()
方法来实现自定义进程行为。
步骤:
- 导入
multiprocessing
模块 - 创建
Process
的子类 - 重写
__init__
方法(可选) - 重写
run()
方法,定义进程执行逻辑 - 实例化自定义进程类
- 启动进程
import multiprocessing
import os
import time
# 自定义进程类
class CustomProcess(multiprocessing.Process):
def __init__(self, task_id, delay):
super().__init__() # 调用父类初始化
self.task_id = task_id
self.delay = delay
def run(self):
"""重写run方法,定义进程执行内容"""
print(f"自定义进程 {self.task_id} (PID: {os.getpid()}) 开始执行")
time.sleep(self.delay)
print(f"自定义进程 {self.task_id} 完成,耗时 {self.delay}秒")
if __name__ == '__main__':
# 创建自定义进程实例
processes = [
CustomProcess(i, i+1) for i in range(3)
]
# 启动所有进程
for p in processes:
p.start()
# 等待进程完成
for p in processes:
p.join()
print("所有自定义进程执行完毕!")
代码解析:
- 继承
multiprocessing.Process
类 - 重写
__init__
方法时,必须调用父类的__init__
run()
方法:定义进程启动后执行的操作- 通过调用
start()
方法启动进程,会自动调用run()
方法 - 这种方法更适合需要封装复杂逻辑的进程
进程创建过程可视化
(PID: 1234)
(PID: 5678)
(PID: 9012)
(PID: 3456)
主进程创建并启动多个子进程,每个子进程拥有独立的PID和内存空间
函数式创建进程
优点:
- 简单直观,适合简单任务
- 代码量少,易于理解
- 不需要创建新类
- 与现有函数集成方便
缺点:
- 不适合复杂逻辑
- 状态管理不够灵活
- 进程相关功能扩展性有限
继承式创建进程
优点:
- 封装性好,适合复杂任务
- 可以添加额外方法和属性
- 更好的代码组织和复用性
- 便于扩展进程功能
缺点:
- 需要创建新类,代码量稍多
- 学习曲线稍陡峭
- 对于简单任务可能过度设计
多进程编程最佳实践
进程间通信
使用Queue、Pipe或共享内存进行进程间通信,避免使用全局变量
资源管理
使用with语句或确保在finally块中释放资源,避免资源泄漏
进程池
对于大量任务,使用multiprocessing.Pool管理进程更高效
重要注意事项
- Windows系统限制:在Windows上必须使用
if __name__ == '__main__'
保护入口点 - 进程间隔离:进程不共享内存,修改父进程变量不会影响子进程
- 资源消耗:创建进程比线程开销更大,进程数量不宜过多
- 进程间通信:需要使用特定机制(Queue、Pipe等)进行数据交换
- 可序列化:传递给子进程的参数和返回值必须是可序列化的
发表评论