深入探究Python多进程编程:Multiprocessing模块基础与实战【第98篇—Multiprocessing模块】

深入探究Python多进程编程:Multiprocessing模块基础与实战

在Python编程中,多进程处理是一项关键的技术,特别是在需要处理大规模数据或执行耗时任务时。为了充分利用多核处理器的优势,Python提供了multiprocessing模块,使得并行编程变得更加容易。本文将深入探讨multiprocessing模块的基础知识,并通过实际代码示例演示其在解决实际问题中的应用。

image-20240224234012221

多进程编程基础

在了解multiprocessing模块之前,我们先来了解一下进程的基本概念。进程是计算机中运行的程序的实例,它拥有独立的内存空间和系统资源。相比于多线程,多进程更容易实现并行处理,因为每个进程都有自己的解释器和全局解释器锁(GIL)。

multiprocessing模块提供了Process类,用于创建和管理进程。以下是一个简单的示例,演示如何使用Process创建并启动两个进程:

from multiprocessing import Process
import osdef print_process_info():print(f"Process ID: {os.getpid()}")print(f"Parent Process ID: {os.getppid()}")if __name__ == "__main__":# 创建两个进程process1 = Process(target=print_process_info)process2 = Process(target=print_process_info)# 启动进程process1.start()process2.start()# 等待两个进程结束process1.join()process2.join()

在这个例子中,我们定义了一个简单的函数print_process_info,该函数用于输出当前进程的ID和父进程的ID。然后,我们创建了两个Process对象,分别代表两个进程,并使用start()方法启动它们。最后,使用join()方法等待两个进程执行完毕。

实战:使用多进程进行数据处理

现在,让我们通过一个实际的例子来展示multiprocessing模块在数据处理中的应用。假设我们有一个需要处理的大型数据集,我们希望通过多进程并行处理来提高处理速度。

from multiprocessing import Pooldef process_data(data_chunk):# 在这里进行数据处理,这里仅作为示例,实际中需要根据具体需求进行修改processed_data = [item * 2 for item in data_chunk]return processed_dataif __name__ == "__main__":# 模拟一个大型数据集data = list(range(1000000))# 定义进程池,指定进程数量num_processes = 4with Pool(num_processes) as pool:# 将数据分割成多个子集,每个子集交给一个进程处理data_chunks = [data[i:i + len(data) // num_processes] for i in range(0, len(data), len(data) // num_processes)]# 使用进程池并行处理数据processed_results = pool.map(process_data, data_chunks)# 合并处理后的结果final_result = [item for sublist in processed_results for item in sublist]# 打印处理后的数据print(final_result[:10])

在这个例子中,我们使用Pool类创建了一个进程池,指定了进程的数量。然后,我们将大型数据集分割成多个子集,每个子集由一个进程处理。使用pool.map()方法并行处理这些子集,最后合并各个进程的处理结果。

代码解析

  • Pool类:进程池的创建和管理类,通过指定进程数量,可以实现并行处理。
  • map()方法:类似于内置函数map(),但是在多进程环境中运行。它将一个可迭代对象分割成多个部分,每个部分由一个进程处理。

通过上述代码解析,我们可以看到multiprocessing模块的核心概念是创建进程、使用进程池并行处理数据。这使得在处理大规模数据时,能够充分利用多核处理器的性能,提高程序的执行效率。

总结起来,multiprocessing模块为Python程序员提供了一种简便而强大的多进程处理方式,通过灵活运用这些工具,我们能够更好地解决涉及大规模数据处理或计算密集型任务的问题。

进程间通信与共享数据

在多进程编程中,不同进程之间通常是相互独立的,但有时候我们需要让它们进行通信或共享数据。multiprocessing模块提供了多种方式来实现进程间通信:

1. 队列(Queue)

队列是多进程之间安全地传递数据的一种方式。以下是一个简单的例子:

from multiprocessing import Process, Queuedef producer(queue):for item in range(5):queue.put(item)def consumer(queue):while True:item = queue.get()if item is None:breakprint(f"Consumed: {item}")if __name__ == "__main__":shared_queue = Queue()# 创建生产者和消费者进程producer_process = Process(target=producer, args=(shared_queue,))consumer_process = Process(target=consumer, args=(shared_queue,))# 启动进程producer_process.start()consumer_process.start()# 等待生产者生产完数据producer_process.join()# 告诉消费者不再有数据shared_queue.put(None)# 等待消费者消费完数据consumer_process.join()
2. 共享内存(Value、Array)

有时候我们需要在多个进程之间共享数据,multiprocessing模块提供了ValueArray来实现这一目的:

from multiprocessing import Process, Value, Arraydef update_shared_data(shared_value, shared_array):shared_value.value += 1for i in range(len(shared_array)):shared_array[i] *= 2if __name__ == "__main__":shared_value = Value('i', 0)  # 整数shared_array = Array('d', [1.0, 2.0, 3.0, 4.0])  # 双精度浮点数组update_process = Process(target=update_shared_data, args=(shared_value, shared_array))update_process.start()update_process.join()print(f"Updated Value: {shared_value.value}")print(f"Updated Array: {list(shared_array)}")

异常处理与资源管理

在多进程编程中,异常处理和资源管理尤为重要。我们需要确保进程在执行过程中的异常能够被捕获,并在进程结束时释放资源。使用tryexcept块以及finally块来实现异常处理和资源管理。

from multiprocessing import Process, Queuedef process_with_exception(queue):try:# 进程执行的代码result = 1 / 0  # 触发一个异常queue.put(result)except Exception as e:# 捕获异常,并将异常信息放入队列queue.put(e)finally:# 释放资源等清理工作print("Clean up and release resources.")if __name__ == "__main__":shared_queue = Queue()process = Process(target=process_with_exception, args=(shared_queue,))process.start()process.join()# 从队列获取进程执行的结果或异常信息result_or_exception = shared_queue.get()print(f"Result or Exception: {result_or_exception}")

性能优化与注意事项

在使用multiprocessing模块进行多进程编程时,为了充分发挥其优势,我们需要注意一些性能优化的技巧和注意事项。

1. 进程池的重用

进程池(Pool)的创建和销毁是有开销的,为了避免频繁创建进程池,可以考虑在程序的生命周期内重用进程池。这可以通过将进程池的创建放在程序的初始化部分,并在程序结束时关闭进程池来实现。

from multiprocessing import Pooldef process_data(data_chunk):# 数据处理逻辑if __name__ == "__main__":num_processes = 4with Pool(num_processes) as pool:# 在整个程序生命周期内重用进程池data_chunks = [...]results = pool.map(process_data, data_chunks)# 进程池会在程序结束时自动关闭
2. 避免过多的进程创建

尽管多进程可以提高程序的并行性,但过多的进程创建也会导致系统资源的消耗和性能下降。在确定进程数量时,需要根据系统的核心数和任务的性质进行合理的选择。可以通过os.cpu_count()获取系统的核心数,并根据具体情况调整进程数量。

import os
from multiprocessing import Pooldef process_data(data_chunk):# 数据处理逻辑if __name__ == "__main__":num_processes = min(os.cpu_count(), 8)  # 最多使用8个核心with Pool(num_processes) as pool:# 进程池的使用逻辑
3. 注意数据的序列化与反序列化开销

在多进程编程中,数据需要在进程之间传递,而这涉及到数据的序列化和反序列化。不同的数据类型和序列化方式会对性能产生影响,因此在选择数据传递方式时需要注意。对于大型数据集,可以考虑使用multiprocessing模块中的Manager类来创建共享的数据结构,以避免不必要的数据复制。

from multiprocessing import Manager, Pooldef process_data(shared_data):# 在多进程中直接使用共享的数据结构if __name__ == "__main__":with Manager() as manager:shared_data = manager.list([...])  # 使用Manager创建共享的列表num_processes = 4with Pool(num_processes) as pool:pool.map(process_data, [shared_data] * num_processes)

跨平台兼容性

multiprocessing模块在大多数平台上都能正常运行,但在一些特殊的情况下可能会遇到一些问题。特别是在Windows系统上,由于其进程创建的机制不同,一些全局变量和共享资源的使用可能需要格外小心。建议在跨平台开发中进行充分的测试和调试,确保程序在不同平台上都能正常运行。

安全性与锁

多进程编程涉及到多个进程同时访问共享资源的情况,因此需要考虑安全性和避免竞争条件。multiprocessing模块提供了锁(Lock)等同步原语,可以用来确保在多个进程之间安全地访问共享资源。

from multiprocessing import Lock, Processshared_value = 0
lock = Lock()def update_shared_value():global shared_valuefor _ in range(100000):with lock:shared_value += 1if __name__ == "__main__":processes = [Process(target=update_shared_value) for _ in range(4)]for process in processes:process.start()for process in processes:process.join()print(f"Final Shared Value: {shared_value}")

在上述例子中,通过Lock确保了对shared_value的安全访问。每个进程在执行更新操作时,都需要先获取锁,更新完成后释放锁,以防止多个进程同时修改共享资源导致的问题。

调试和日志记录

在多进程编程中,由于多个进程同时运行,调试可能会变得更加复杂。为了更好地定位问题,可以使用logging模块来记录日志,以及适当的调试工具。同时,了解进程间通信的机制,以便在有需要时获取进程的状态信息。

import logging
from multiprocessing import Processdef worker_function():logging.info("Worker process is starting.")# 进程执行的代码logging.info("Worker process is finishing.")if __name__ == "__main__":logging.basicConfig(level=logging.INFO)process = Process(target=worker_function)process.start()process.join()

在上述例子中,我们使用了logging模块记录了进程的启动和结束信息。通过适当设置日志级别,可以灵活控制记录的信息量。

异步与多进程

在一些特定的场景中,异步编程可能比多进程更为适用。异步编程通过单线程实现并发,可以有效提高程序的性能。在Python中,asyncio库提供了异步编程的支持。但需要注意,异步编程适用于I/O密集型任务,而多进程适用于计算密集型任务。

import asyncioasync def async_worker():# 异步任务await asyncio.sleep(1)print("Async worker finished.")if __name__ == "__main__":asyncio.run(async_worker())

避免全局变量的滥用

全局变量在多进程编程中可能引发一些问题,尤其是在涉及到进程间通信时。由于每个进程拥有独立的地址空间,全局变量的修改在不同进程中并不互相影响。在需要共享数据时,应使用multiprocessing模块提供的共享数据结构。

from multiprocessing import Value, Processshared_value = Value('i', 0)def update_shared_value():global shared_valuewith shared_value.get_lock():shared_value.value += 1if __name__ == "__main__":processes = [Process(target=update_shared_value) for _ in range(4)]for process in processes:process.start()for process in processes:process.join()print(f"Final Shared Value: {shared_value.value}")

在上述例子中,通过Valueget_lock()方法获取锁,确保对共享数据的安全访问。

子进程的异常处理

当子进程发生异常时,可以通过Processexitcode属性获取其退出码。一般而言,非0的退出码表示进程异常退出。

from multiprocessing import Process
import timedef process_with_exception():time.sleep(1)raise Exception("Something went wrong!")if __name__ == "__main__":process = Process(target=process_with_exception)process.start()process.join()if process.exitcode == 0:print("Process executed successfully.")else:print(f"Process exited with code {process.exitcode}")

总结:

本文深入探讨了Python中多进程编程的基础知识,以及如何使用multiprocessing模块解决实际问题。通过详细的代码示例,读者了解了如何创建和管理进程、利用进程池进行数据处理、实现进程间通信和共享数据。同时,介绍了性能优化、跨平台兼容性、异常处理与资源管理等方面的注意事项,帮助读者更好地应用多进程编程。

文章强调了进程池的重用、避免过多的进程创建、注意数据的序列化与反序列化开销等性能优化技巧。跨平台兼容性、安全性与锁的考虑以及调试和日志记录等内容也被详细讨论。此外,文章还提及了异步编程与多进程的比较,以及在多进程编程中避免全局变量滥用的重要性。

最后,通过总结子进程的异常处理等关键点,强调了在多进程编程中需要注意的一些细节。通过合理运用文章中提到的知识点,读者可以更高效地应对多进程编程中的挑战,提高程序性能和可维护性。希望本文能够帮助读者更深入地理解和应用Python中的多进程编程技术。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2807259.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Linux之JAVA环境配置jdkTomcatMySQL

目录 一. 安装jdk 1.1 查询是否有jdk 1.2 解压 1.3 配置环境变量 二. 安装Tomcat(开机自启动) 2.1 解压 2.2 启动tomcat 2.3 防火墙设置 2.4 创建启动脚本(设置自启动,服务器开启即启动) 三. MySQL安装(…

联想开天昭阳N4620Z笔记本如何恢复出厂麒麟操作系统(图解)

联想开天昭阳N4620Z笔记本简单参数: 中央处理器:KX-6640MA G2 内存:8GB 固态硬盘:512GB SSD 显示器:14.0”FHD 电池:4Cell 操作系统:麒麟KOS中文RTM(试用版) 此款笔…

Unity与Android交互通信系列(5)

在前述文章中,已经使用了AndroidJavaProxy代理接口,本节我们将详细的介绍AndroidJavaProxy代理的用法。正如其名,AndroidJavaProxy是一个代理,它在Android端代码与Unity端代码交互中起一个桥接作用。其一般用法为在Java代码中定义…

网络原理——HTTP

1. 什么是HTTP协议 HTTP是应用层的协议。Java最主要的应用场景是做网站,而网站由 后端(HTTP服务器) 和 前端(浏览器)组成,HTTP协议就是负责这里后端和前端的数据交互。 HTTP3.0 之前在传输层是通过 TCP传…

并发编程(5)共享模型之不可变

7 共享模型之不可变 本章内容 不可变类的使用不可变类设计无状态类设计 7.1 日期转换的问题 问题提出 下面的代码在运行时,由于 SimpleDateFormat 不是线程安全的, 有很大几率出现 java.lang.NumberFormatException 或者出现不正确的日期解析结果,…

PyQt5图片浏览器

PyQt5图片浏览器 实现方式功能实现具体代码 界面实现pillow源码修改ImageQt错误主页面布局 项目开源地址 分享一个图片浏览器 实现方式 qt本身有一个QGraphicsView类用来当做视图框架。 具体参考:如何在pyqt中使用 QGraphicsView 实现图片查看器 不过大佬给的例子…

微信小程序 uniapp+vue餐厅美食就餐推荐系统

本论文根据系统的开发流程以及一般论文的结构分为三个部分,第一个部分为摘要、外文翻译、目录;第二个部分为正文;第三个部分为致谢和参考文献。其中正文部分包括: (1)绪论,对课题背景、意义、目…

eBPF实践篇之基础概念

文章目录 前言基本概念eBPF的生命周期之旅最后 前言 eBPF 是一门革命性的技术,可以在不修改内核源代码或者加载内核模块的情况下,安全和高效地拓展和增强Linux内核的功能,我们主要聚焦在eBPF在网络传输上的应用和实践🚀 基本概念…

AI时代显卡如何选择,B100、H200、L40S、A100、H100、V100 含架构技术和性能对比

AI时代显卡如何选择,B100、H200、L40S、A100、H100、V100 含架构技术和性能对比。 英伟达系列显卡大解析B100、H200、L40S、A100、A800、H100、H800、V100如何选择,含架构技术和性能对比带你解决疑惑。 近期,AIGC领域呈现出一片繁荣景象&a…

企业型多域名SSL证书

多域名SSL证书是目前市场上用的比较多的一种,主要解决多个不同规则的域名申请,但不适合主域名(根域名)相同的域名,因为这种域名直接申请通配符。 企业型其实就是OV类型或者EV类型,由于在CA/B产品名称规范中…

【k8s资源调度-HPA(自动扩缩容)】

1、HPA可以做什么? 通过观察pod的cpu、内存使用率或自定义metrics指标进行自动的扩容或缩容pod的数量。通常用于Deployment,不适用于无法扩/缩容的对象,如DaemonSet。控制管理器每隔30s(可以通过-horizontal-pod-autoscaler–sync-period修改…

Ubuntu20.04和Windows11下配置StarCraft II环境

1.Ubuntu20.04 根据下面这篇博客就可以顺利安装: 强化学习实战(九) Linux下配置星际争霸Ⅱ环境https://blog.csdn.net/weixin_39059031/article/details/117247635?spm1001.2014.3001.5506 Ubuntu下显示游戏界面目前还没有解决掉。 大家可以根据以下链接看看能…

Jenkins详解

目录 一、Jenkins CI/CD 1、 Jenkins CI/CD 流程图 2、介绍 Jenkins 1、Jenkins概念 2、Jenkins目的 3、特性 4、产品发布流程 3、安装Jenkins 1、安装JDK 2、安装tomcat 3.安装maven 4安装jenkins 5.启动tomcat,并页面访问 5.添加节点 一、Jenkins CI/…

[深度学习]yolov9+bytetrack+pyqt5实现目标追踪

【简介】 目标追踪简介 目标追踪是计算机视觉领域中的一个热门研究方向,它涉及到从视频序列中实时地、准确地跟踪目标对象的位置和运动轨迹。随着深度学习技术的快速发展,基于深度学习的目标追踪方法逐渐展现出强大的性能。其中,YOLOv9&…

web安全学习笔记【16】——信息打点(6)

信息打点-语言框架&开发组件&FastJson&Shiro&Log4j&SpringBoot等[1] #知识点: 1、业务资产-应用类型分类 2、Web单域名获取-接口查询 3、Web子域名获取-解析枚举 4、Web架构资产-平台指纹识别 ------------------------------------ 1、开源-C…

K线实战分析系列之一:标准K线图的识别

K线实战分析系列之一:标准K线图的识别 一、阳线和阴线二、光头K线三、光脚K线四、光头光脚大阳线五、纺锤线六、十字线 一、阳线和阴线 二、光头K线 如果某根K线没有上影线,就叫它光头k线 三、光脚K线 某一根K线没有下影线就叫它光脚K线 四、光头光…

广联达Linkworks GetAllData 信息泄露漏洞

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

【CSS-语法】

CSS-语法 ■ CSS简介■ CSS 实例■ CSS id 和 class选择器■ CSS 样式表■ 外部样式表(External style sheet)■ 内部样式表(Internal style sheet)■ 内联样式(Inline style)■ 多重样式 ■ CSS 文本■ CSS 文本颜色■ CSS 文本的对齐方式■ CSS 文本修饰■ CSS 文本转换■ CS…

力扣1290. 二进制链表转整数

Problem: 1290. 二进制链表转整数 文章目录 题目描述思路复杂度Code 题目描述 思路 1.记录一个变量res初始化为0,指针p指向链表头; 2.循环每次res res * 2 p -> val;p p -> next;(充分利用二进制数的特性;其中利用指针先…

Flutter开发进阶之Package

Flutter开发进阶之Package 通常我们在Flutter开发中需要将部分功能与整体项目隔离,一般有两种方案Plugin和Package,Application是作为主体项目,Module是作为原生项目接入Flutter模块。 当独立模块不需要与原生项目通讯只需要Plugin就可以&a…