1.引言
刚刚学完python的多线程编程内容,怎么还需要多进程呢?都是并行提升应用处理性能的方案。
这里不得不再次说起python的全局解释器锁GIL的存在,它要求同一时刻只能有一个线程执行python字节码,如果是I/O密集型的任务,这没有问题!当一个线程在待等I/O的时间可以切换另外一个线程执行。但如果是CPU密集型任务,那就只能靠多进程方案了。
选择多进程方案理由,有这么几个:
- CPU密集型任务,比如数学计算,图像处理
- 充分利用多核CPU,让每个进程独立使用CPU核心
- 提供应用稳定性,每个进程都有自己的独立空间,单个进程崩溃不影响其它进程
当然,任何方案都有两面性,多进程方案有它的缺点,进程比起线程,要消耗更多资源。
2.多进程案例
2.1.入门案例
python中提供multiprocessing模块,支持多进程编程。
python
import multiprocessing
import os
# 定义进程任务,模拟耗时计算
def task(name):
print(f"子进程 {name} PID: {os.getpid()}")
# 模拟耗时计算
result = sum([i * i for i in range(1000000)])
print(f"子进程 {name} 计算结果: {result}")
# 启动执行
if __name__ == '__main__':
print(f"主进程 PID: {os.getpid()}")
# 创建子进程,启动,等待执行完成
p = multiprocessing.Process(target=task, args=("进程1",))
p.start()
p.join()
print("所有进程执行完毕")
2.2.进程池
和线程池一样,本质是解决提升资源利用率的方案,相比较起来,进程要比线程消耗更多的资源,是一个重量级的方案。对于资源的复用更有实际意义。
python
from multiprocessing import Pool
import time
import os
# 定义任务
def square(x):
print(f"进程PID:{os.getpid()}执行计算任务 {x} 的平方")
time.sleep(1) # 模拟耗时操作
return x * x
# 主函数
if __name__ == '__main__':
with Pool(3) as pool: # 创建3个进程的池
numbers = [1, 2, 3, 4, 5]
results = pool.map(square, numbers)
print("计算结果:", results)
2.3.进程间通信
多任务并发处理,往往有彼此协作通信的需求,不管是多线程,还是多进程。多线程我们可以通过共享内存,多进程可以通过队列Queue方案。
生产者,消费者示例:
python
from multiprocessing import Process, Queue
import os
# 定义生产者任务
def produce(q):
for i in range(5):
q.put(f"产品:{i}")
print(f"生产者 {os.getpid()}生产产品:{i}")
# 定义消费者
def consume(q):
while True:
item = q.get()
if item is None:
break
print(f"消费者PID:{os.getpid()}消费{item}")
# 主函数
if __name__ == '__main__':
q = Queue()
producer = Process(target=produce, args=(q,))
consumer = Process(target=consume, args=(q,))
producer.start()
consumer.start()
producer.join()
q.put(None) # 发送结束信号
consumer.join()